mirror of
https://github.com/taskgenius/taskgenius-plugin.git
synced 2026-07-22 06:40:25 +00:00
feat(onboarding): enhance intro animation and fix mode selection UI
- Add fade-out animation for intro typing lines with configurable delays - Integrate mode selection directly into intro typing flow - Fix grid layout and responsive design for mode/placement selection cards - Remove tg-v2-card dependency in favor of dedicated onboarding styles - Add visual previews for Fluent and Legacy modes with icons - Translate UI text from Chinese to English for consistency - Improve card selection states with proper event handling - Add flex layouts to ensure proper vertical stacking in cards
This commit is contained in:
parent
3fcf2f9705
commit
491e2a6189
8 changed files with 3867 additions and 86 deletions
Binary file not shown.
|
|
@ -2,13 +2,15 @@ import { t } from "@/translations/helper";
|
|||
|
||||
export class IntroTyping {
|
||||
private timers: number[] = [];
|
||||
private onComplete?: () => void;
|
||||
|
||||
cleanup() {
|
||||
this.timers.forEach((id) => window.clearTimeout(id));
|
||||
this.timers = [];
|
||||
}
|
||||
|
||||
render(container: HTMLElement) {
|
||||
render(container: HTMLElement, onComplete?: () => void) {
|
||||
this.onComplete = onComplete;
|
||||
container.empty();
|
||||
const wrap = container.createDiv({cls: "intro-typing"});
|
||||
|
||||
|
|
@ -21,22 +23,51 @@ export class IntroTyping {
|
|||
{
|
||||
el: line1,
|
||||
text: t("Hi,"),
|
||||
speed: 35
|
||||
speed: 35,
|
||||
keepAfter: true
|
||||
},
|
||||
{
|
||||
el: line2,
|
||||
text: t("Thank you for using Task Genius"),
|
||||
speed: 25
|
||||
speed: 25,
|
||||
keepAfter: false // Will be removed after line3
|
||||
},
|
||||
{
|
||||
el: line3,
|
||||
text: t("In the following steps, you will gradually set up Task Genius to get a more suitable environment for you"),
|
||||
speed: 20
|
||||
speed: 20,
|
||||
keepAfter: false, // Will be removed after line3
|
||||
delayNext: 5000, // Wait for fade out to complete (600ms wait + 500ms fade + 100ms buffer)
|
||||
afterComplete: () => {
|
||||
// Fade out and remove line2 and line3 after a brief pause
|
||||
const id = window.setTimeout(() => {
|
||||
line1.addClass("intro-line-fadeout");
|
||||
line2.addClass("intro-line-fadeout");
|
||||
line3.addClass("intro-line-fadeout");
|
||||
const id2 = window.setTimeout(() => {
|
||||
line1.remove();
|
||||
line2.remove();
|
||||
line3.remove();
|
||||
}, 2000); // Match CSS transition duration
|
||||
this.timers.push(id2);
|
||||
}, 3000);
|
||||
this.timers.push(id);
|
||||
}
|
||||
},
|
||||
{
|
||||
el: line4,
|
||||
text: t("In the current version, Task Genius provides a brand new visual and interactive experience: Fluent; while also providing the option to return to the previous interface. Which one do you prefer?"),
|
||||
speed: 20
|
||||
speed: 20,
|
||||
keepAfter: true,
|
||||
afterComplete: () => {
|
||||
// Trigger callback to show mode selection buttons
|
||||
if (this.onComplete) {
|
||||
const id = window.setTimeout(() => {
|
||||
this.onComplete?.();
|
||||
}, 300);
|
||||
this.timers.push(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -46,10 +77,16 @@ export class IntroTyping {
|
|||
/**
|
||||
* AI-style streaming text effect with smooth character fade-in
|
||||
*/
|
||||
private aiStreamSequence(seq: { el: HTMLElement; text: string; speed: number }[]) {
|
||||
private aiStreamSequence(seq: {
|
||||
el: HTMLElement;
|
||||
text: string;
|
||||
speed: number;
|
||||
afterComplete?: () => void;
|
||||
delayNext?: number
|
||||
}[]) {
|
||||
const streamLine = (idx: number) => {
|
||||
if (idx >= seq.length) return;
|
||||
const {el, text, speed} = seq[idx];
|
||||
const {el, text, speed, afterComplete, delayNext} = seq[idx];
|
||||
|
||||
// Clear and prepare element
|
||||
el.empty();
|
||||
|
|
@ -96,8 +133,15 @@ export class IntroTyping {
|
|||
cursor.remove();
|
||||
el.removeClass("is-streaming");
|
||||
el.addClass("stream-complete");
|
||||
// Small delay before next line
|
||||
const id = window.setTimeout(() => streamLine(idx + 1), 300);
|
||||
|
||||
// Execute afterComplete callback if provided
|
||||
if (afterComplete) {
|
||||
afterComplete();
|
||||
}
|
||||
|
||||
// Delay before next line (custom or default 300ms)
|
||||
const nextDelay = delayNext !== undefined ? delayNext : 300;
|
||||
const id = window.setTimeout(() => streamLine(idx + 1), nextDelay);
|
||||
this.timers.push(id);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,64 +3,71 @@ import { t } from "@/translations/helper";
|
|||
export type UIMode = 'fluent' | 'legacy';
|
||||
|
||||
export class ModeSelection {
|
||||
render(
|
||||
container: HTMLElement,
|
||||
current: UIMode | null,
|
||||
onSelect: (mode: UIMode) => void
|
||||
) {
|
||||
container.empty();
|
||||
render(
|
||||
container: HTMLElement,
|
||||
current: UIMode | null,
|
||||
onSelect: (mode: UIMode) => void
|
||||
) {
|
||||
container.empty();
|
||||
|
||||
const section = container.createDiv({ cls: "mode-selection" });
|
||||
section.createEl("h2", { text: t("选择界面风格"), cls: "section-title" });
|
||||
section.createEl("p", {
|
||||
text: t("在下方选择你更喜欢的视觉与交互风格:全新的 Fluent 或 传统的 Legacy"),
|
||||
cls: "section-desc",
|
||||
});
|
||||
const section = container.createDiv({cls: "mode-selection"});
|
||||
|
||||
const options = section.createDiv({ cls: "mode-options" });
|
||||
const options = section.createDiv({cls: "mode-options"});
|
||||
|
||||
const card = (
|
||||
mode: UIMode,
|
||||
title: string,
|
||||
desc: string,
|
||||
icon: string
|
||||
) => {
|
||||
const el = options.createDiv({ cls: `mode-card tg-v2-card mode-${mode}` });
|
||||
const header = el.createDiv({ cls: "tg-v2-card-header" });
|
||||
const titleEl = header.createDiv({ cls: "tg-v2-card-title" });
|
||||
titleEl.setText(title);
|
||||
const body = el.createDiv({ cls: "mode-card-body" });
|
||||
const preview = body.createDiv({ cls: "mode-card-preview" });
|
||||
preview.setText(" "); // 预览区域占位,后续可替换为图片
|
||||
const description = body.createDiv({ cls: "mode-card-desc" });
|
||||
description.setText(desc);
|
||||
const card = (
|
||||
mode: UIMode,
|
||||
title: string,
|
||||
desc: string,
|
||||
icon: string
|
||||
) => {
|
||||
const el = options.createDiv({cls: `mode-card mode-${mode}`});
|
||||
const header = el.createDiv({cls: "mode-card-header"});
|
||||
const titleEl = header.createDiv({cls: "mode-card-title"});
|
||||
titleEl.setText(title);
|
||||
const body = el.createDiv({cls: "mode-card-body"});
|
||||
const preview = body.createDiv({cls: "mode-card-preview"});
|
||||
// Visual representation for each mode
|
||||
if (mode === "fluent") {
|
||||
preview.innerHTML = "✨"; // Sparkles for modern
|
||||
} else {
|
||||
preview.innerHTML = "☰"; // Menu icon for traditional
|
||||
}
|
||||
const description = body.createDiv({cls: "mode-card-desc"});
|
||||
description.setText(desc);
|
||||
|
||||
if (current === mode) el.addClass("is-selected");
|
||||
el.addEventListener("click", () => onSelect(mode));
|
||||
el.setAttr("tabindex", "0");
|
||||
el.addClass("clickable-icon");
|
||||
return el;
|
||||
};
|
||||
if (current === mode) el.addClass("is-selected");
|
||||
el.addEventListener("click", () => {
|
||||
// Remove previous selection
|
||||
options.querySelectorAll('.mode-card').forEach(card => {
|
||||
card.removeClass('is-selected');
|
||||
});
|
||||
el.addClass('is-selected');
|
||||
onSelect(mode);
|
||||
});
|
||||
el.setAttr("tabindex", "0");
|
||||
el.addClass("clickable-icon");
|
||||
return el;
|
||||
};
|
||||
|
||||
card(
|
||||
"fluent",
|
||||
t("Fluent(现代与灵动)"),
|
||||
t("全新的信息架构、导航与交互,配合更优雅的样式与动画。"),
|
||||
"sparkles"
|
||||
);
|
||||
card(
|
||||
"legacy",
|
||||
t("Legacy(经典与稳定)"),
|
||||
t("延续过往界面与交互风格,保持熟悉的使用体验。"),
|
||||
"layout"
|
||||
);
|
||||
card(
|
||||
"fluent",
|
||||
t("Fluent (Modern & Dynamic)"),
|
||||
t("New information architecture, navigation and interaction, with more elegant styles and animations."),
|
||||
"sparkles"
|
||||
);
|
||||
card(
|
||||
"legacy",
|
||||
t("Legacy (Classic & Stable)"),
|
||||
t("Continue the previous interface and interaction style, maintaining a familiar user experience."),
|
||||
"layout"
|
||||
);
|
||||
|
||||
// 小提示
|
||||
const tips = section.createDiv({ cls: "mode-tips" });
|
||||
tips.createEl("p", {
|
||||
text: t("你可以在设置中随时切换这些选项。我们会尽量保持迁移的平滑与安全。"),
|
||||
cls: "text-muted",
|
||||
});
|
||||
}
|
||||
// Tips
|
||||
const tips = section.createDiv({cls: "mode-tips"});
|
||||
tips.createEl("p", {
|
||||
text: t("You can switch these options at any time in settings. We'll try to keep the migration smooth and safe."),
|
||||
cls: "text-muted",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -281,15 +281,30 @@ export class OnboardingModal extends Modal {
|
|||
}
|
||||
|
||||
/**
|
||||
* New: Intro typing step
|
||||
* New: Intro typing step - shows typing animation then mode selection
|
||||
*/
|
||||
private displayIntroTypingStep() {
|
||||
// this.headerEl.createEl("h1", {text: t("Welcome")});
|
||||
this.introTyping.render(this.onboardingContentEl);
|
||||
// Hide footer buttons during intro animation
|
||||
this.footerEl.style.display = 'none';
|
||||
|
||||
// Render typing animation
|
||||
this.introTyping.render(this.onboardingContentEl, () => {
|
||||
// After typing completes, show mode selection in same container
|
||||
const modeContainer = this.onboardingContentEl.createDiv({
|
||||
cls: "intro-mode-selection-container"
|
||||
});
|
||||
|
||||
this.modeSelection.render(modeContainer, this.state.uiMode as any, (mode) => {
|
||||
this.state.uiMode = mode;
|
||||
// Show footer with Next button after selection
|
||||
this.footerEl.style.display = '';
|
||||
this.updateButtonStates();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* New: Mode selection (Fluent vs Legacy)
|
||||
* New: Mode selection (Fluent vs Legacy) - standalone step (if needed)
|
||||
*/
|
||||
private displayModeSelectionStep() {
|
||||
this.headerEl.createEl("h1", {text: t("Choose Your Interface Style")});
|
||||
|
|
@ -485,9 +500,13 @@ export class OnboardingModal extends Modal {
|
|||
}
|
||||
|
||||
// Branching for intro/mode/placement
|
||||
// Intro step now includes mode selection, so skip MODE_SELECT step
|
||||
if (step === OnboardingStep.INTRO) {
|
||||
this.state.currentStep = OnboardingStep.MODE_SELECT;
|
||||
this.state.currentStep = this.state.uiMode === 'fluent'
|
||||
? OnboardingStep.FLUENT_PLACEMENT
|
||||
: OnboardingStep.USER_LEVEL_SELECT;
|
||||
} else if (step === OnboardingStep.MODE_SELECT) {
|
||||
// This step is now integrated into INTRO, but keep for backward compatibility
|
||||
this.state.currentStep = this.state.uiMode === 'fluent'
|
||||
? OnboardingStep.FLUENT_PLACEMENT
|
||||
: OnboardingStep.USER_LEVEL_SELECT;
|
||||
|
|
|
|||
|
|
@ -349,17 +349,30 @@ export class OnboardingView extends ItemView {
|
|||
}
|
||||
|
||||
/**
|
||||
* New: Intro typing step
|
||||
* New: Intro typing step - shows typing animation then mode selection
|
||||
*/
|
||||
private displayIntroTypingStep() {
|
||||
// Header minimal
|
||||
// this.onboardingHeaderEl.createEl("h1", {text: t("Welcome")});
|
||||
// Content typing animation
|
||||
this.introTyping.render(this.onboardingContentEl);
|
||||
// Hide footer buttons during intro animation
|
||||
this.footerEl.style.display = 'none';
|
||||
|
||||
// Render typing animation
|
||||
this.introTyping.render(this.onboardingContentEl, () => {
|
||||
// After typing completes, show mode selection in same container
|
||||
const modeContainer = this.onboardingContentEl.createDiv({
|
||||
cls: "intro-mode-selection-container"
|
||||
});
|
||||
|
||||
this.modeSelection.render(modeContainer, this.state.uiMode as any, (mode) => {
|
||||
this.state.uiMode = mode;
|
||||
// Show footer with Next button after selection
|
||||
this.footerEl.style.display = '';
|
||||
this.updateButtonStates();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* New: Mode selection (Fluent vs Legacy) with preview cards
|
||||
* New: Mode selection (Fluent vs Legacy) with preview cards - standalone step (if needed)
|
||||
*/
|
||||
private displayModeSelectionStep() {
|
||||
// Header
|
||||
|
|
@ -580,12 +593,19 @@ export class OnboardingView extends ItemView {
|
|||
}
|
||||
|
||||
// Custom flow for new intro/mode/placement steps
|
||||
// Intro step now includes mode selection, so skip MODE_SELECT step
|
||||
if (step === OnboardingStep.INTRO) {
|
||||
// After intro, check if user has changes to decide next step
|
||||
this.state.currentStep = this.state.userHasChanges
|
||||
? OnboardingStep.SETTINGS_CHECK
|
||||
: OnboardingStep.MODE_SELECT;
|
||||
// After intro (which includes mode selection), check if user has changes
|
||||
if (this.state.userHasChanges) {
|
||||
this.state.currentStep = OnboardingStep.SETTINGS_CHECK;
|
||||
} else {
|
||||
// Skip MODE_SELECT and go directly to FLUENT_PLACEMENT or USER_LEVEL_SELECT
|
||||
this.state.currentStep = this.state.uiMode === 'fluent'
|
||||
? OnboardingStep.FLUENT_PLACEMENT
|
||||
: OnboardingStep.USER_LEVEL_SELECT;
|
||||
}
|
||||
} else if (step === OnboardingStep.MODE_SELECT) {
|
||||
// This step is now integrated into INTRO, but keep for backward compatibility
|
||||
this.state.currentStep = this.state.uiMode === 'fluent'
|
||||
? OnboardingStep.FLUENT_PLACEMENT
|
||||
: OnboardingStep.USER_LEVEL_SELECT;
|
||||
|
|
|
|||
3207
src/experimental/v2/TaskViewV2.backup.ts
Normal file
3207
src/experimental/v2/TaskViewV2.backup.ts
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -45,8 +45,7 @@
|
|||
/* Header section */
|
||||
.onboarding-modal .onboarding-header,
|
||||
.onboarding-view .onboarding-header {
|
||||
padding: var(--onboarding-spacing) var(--onboarding-spacing) var(--size-4-2)
|
||||
var(--onboarding-spacing);
|
||||
padding: var(--onboarding-spacing) var(--onboarding-spacing) var(--size-4-2) var(--onboarding-spacing);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
|
@ -64,15 +63,16 @@
|
|||
padding: var(--onboarding-spacing);
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
/* Footer section */
|
||||
.onboarding-modal .onboarding-footer,
|
||||
.onboarding-view .onboarding-footer {
|
||||
padding: var(--size-4-2) var(--onboarding-spacing) var(--onboarding-spacing)
|
||||
var(--onboarding-spacing);
|
||||
border-top: var(--modal-border-width) solid
|
||||
var(--background-modifier-border);
|
||||
padding: var(--size-4-2) var(--onboarding-spacing) var(--onboarding-spacing) var(--onboarding-spacing);
|
||||
border-top: var(--modal-border-width) solid var(--background-modifier-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
|
@ -1049,6 +1049,25 @@
|
|||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Mode and Placement Selection - Mobile Layout */
|
||||
.mode-options,
|
||||
.placement-options {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.mode-card-preview {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.placement-card-preview {
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.mode-card,
|
||||
.placement-card {
|
||||
padding: var(--size-4-3);
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
|
|
@ -1094,6 +1113,225 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
/* Mode Selection - Fluent vs Legacy */
|
||||
/* ============================== */
|
||||
|
||||
.mode-selection {
|
||||
margin-bottom: var(--onboarding-spacing);
|
||||
}
|
||||
|
||||
.mode-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: var(--onboarding-spacing);
|
||||
margin: var(--onboarding-spacing) 0;
|
||||
}
|
||||
|
||||
.mode-card {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--onboarding-border-radius);
|
||||
padding: var(--onboarding-spacing);
|
||||
cursor: pointer;
|
||||
transition: var(--onboarding-transition);
|
||||
background: var(--background-primary);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mode-card:hover {
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.mode-card.is-selected {
|
||||
border-color: var(--interactive-accent);
|
||||
background: var(--background-modifier-hover);
|
||||
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.mode-card-header {
|
||||
margin-bottom: var(--size-4-3);
|
||||
}
|
||||
|
||||
.mode-card-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
margin-bottom: var(--size-4-2);
|
||||
}
|
||||
|
||||
.mode-card-body {
|
||||
margin-top: var(--size-4-2);
|
||||
}
|
||||
|
||||
.mode-card-preview {
|
||||
height: 140px;
|
||||
background: var(--background-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
margin-bottom: var(--size-4-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 3em;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mode-fluent .mode-card-preview {
|
||||
background: linear-gradient(135deg, var(--background-secondary) 0%, var(--background-modifier-hover) 100%);
|
||||
}
|
||||
|
||||
.mode-legacy .mode-card-preview {
|
||||
background: var(--background-secondary);
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.mode-card-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
line-height: 1.5;
|
||||
min-height: 3em;
|
||||
}
|
||||
|
||||
/* Section shared styles */
|
||||
.section-title {
|
||||
font-size: 1.25em;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
margin-bottom: var(--size-4-2);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.95em;
|
||||
margin-bottom: var(--size-4-4);
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.mode-tips,
|
||||
.placement-tips {
|
||||
margin-top: var(--onboarding-spacing);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mode-tips p,
|
||||
.placement-tips p {
|
||||
margin: 0;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
/* Placement Selection - Sideleaves vs Inline */
|
||||
/* ============================== */
|
||||
|
||||
.placement-selection {
|
||||
margin-bottom: var(--onboarding-spacing);
|
||||
}
|
||||
|
||||
.placement-options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: var(--onboarding-spacing);
|
||||
margin: var(--onboarding-spacing) 0;
|
||||
}
|
||||
|
||||
.placement-card {
|
||||
border: 1px solid var(--background-modifier-border);
|
||||
border-radius: var(--onboarding-border-radius);
|
||||
padding: var(--onboarding-spacing);
|
||||
cursor: pointer;
|
||||
transition: var(--onboarding-transition);
|
||||
background: var(--background-primary);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.placement-card:hover {
|
||||
border-color: var(--interactive-accent);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.placement-card.is-selected {
|
||||
border-color: var(--interactive-accent);
|
||||
background: var(--background-modifier-hover);
|
||||
box-shadow: 0 0 0 2px rgba(var(--interactive-accent-rgb), 0.2);
|
||||
}
|
||||
|
||||
.placement-card-header {
|
||||
margin-bottom: var(--size-4-3);
|
||||
}
|
||||
|
||||
.placement-card-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: 600;
|
||||
color: var(--text-normal);
|
||||
margin-bottom: var(--size-4-2);
|
||||
}
|
||||
|
||||
.placement-card-body {
|
||||
margin-top: var(--size-4-2);
|
||||
}
|
||||
|
||||
.placement-card-preview {
|
||||
height: 100px;
|
||||
background: var(--background-secondary);
|
||||
border-radius: var(--radius-s);
|
||||
margin-bottom: var(--size-4-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-muted);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Visual representations for placement modes */
|
||||
.placement-sideleaves .placement-card-preview {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 2fr 1fr;
|
||||
gap: 4px;
|
||||
padding: var(--size-4-2);
|
||||
}
|
||||
|
||||
.placement-preview-col {
|
||||
background: var(--background-modifier-border);
|
||||
border-radius: 2px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.placement-preview-col.placement-preview-main {
|
||||
background: var(--interactive-accent);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.placement-inline .placement-card-preview {
|
||||
padding: var(--size-4-2);
|
||||
}
|
||||
|
||||
.placement-preview-single {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--interactive-accent);
|
||||
opacity: 0.3;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.placement-card-desc {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9em;
|
||||
line-height: 1.5;
|
||||
min-height: 3em;
|
||||
}
|
||||
|
||||
/* ============================== */
|
||||
/* AI-Style Intro Typing Animation */
|
||||
/* ============================== */
|
||||
|
|
@ -1144,8 +1382,8 @@
|
|||
filter: blur(4px);
|
||||
transform: translateY(-2px);
|
||||
transition: opacity 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
filter 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
filter 0.4s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
@ -1197,6 +1435,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* Fade out animation for removing lines */
|
||||
.intro-line-fadeout {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
transition: opacity 0.5s ease-out, transform 0.5s ease-out;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Mode selection container that appears after intro typing */
|
||||
.intro-mode-selection-container {
|
||||
animation: fadeInFromBottom 0.6s ease-out;
|
||||
animation-fill-mode: both;
|
||||
width: clamp(400px, 40vw, 40%);
|
||||
}
|
||||
|
||||
@keyframes fadeInFromBottom {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Enhanced readability on different themes */
|
||||
.theme-dark .intro-line-1 {
|
||||
color: var(--text-normal);
|
||||
|
|
|
|||
222
styles.css
222
styles.css
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue