refactor(onboarding): decompose fluent onboarding into progressive steps

Breaking down the Fluent UI onboarding process into smaller, focused steps
for better user guidance and comprehension:

- Split monolithic Fluent components step into 6 progressive steps
- Add individual step components for each UI section (Overview, Workspace
  Selector, Main Navigation, Projects, Other Views, Top Navigation)
- Enhance preview components with interactive selection and focus/dim modes
- Improve visual hierarchy with targeted highlighting for each step
- Add keyboard support and accessibility attributes to preview components
- Fix transition timing to show config check only at appropriate steps

This change provides a more guided and digestible onboarding experience
by presenting UI concepts progressively rather than all at once.
This commit is contained in:
Boninall 2025-10-03 22:58:28 +08:00
parent 962623b3e8
commit 1814877969
15 changed files with 498 additions and 168 deletions

BIN
media/fluent-layout.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View file

@ -3,14 +3,20 @@ import { OnboardingConfig, OnboardingConfigMode } from "@/managers/onboarding-ma
export enum OnboardingStep {
INTRO = 0,
MODE_SELECT = 1,
FLUENT_PLACEMENT = 2,
FLUENT_COMPONENTS = 3,
SETTINGS_CHECK = 4,
USER_LEVEL_SELECT = 5,
FILE_FILTER = 6,
CONFIG_PREVIEW = 7,
TASK_CREATION_GUIDE = 8,
COMPLETE = 9,
// Fluent progressive steps
FLUENT_OVERVIEW = 2,
FLUENT_WS_SELECTOR = 3,
FLUENT_MAIN_NAV = 4,
FLUENT_PROJECTS = 5,
FLUENT_OTHER_VIEWS = 6,
FLUENT_TOPNAV = 7,
// Config & rest
SETTINGS_CHECK = 8,
USER_LEVEL_SELECT = 9,
FILE_FILTER = 10,
CONFIG_PREVIEW = 11,
TASK_CREATION_GUIDE = 12,
COMPLETE = 13,
}
export interface OnboardingState {
@ -132,47 +138,37 @@ export class OnboardingController {
// Determine next step based on current step and state
switch (currentStep) {
case OnboardingStep.INTRO:
// Mode selection is now inline in INTRO step
// So we skip MODE_SELECT and go directly based on selected mode
if (this.state.uiMode === 'fluent') {
nextStep = OnboardingStep.FLUENT_PLACEMENT;
} else {
// Legacy mode: check for existing changes
if (this.state.userHasChanges) {
nextStep = OnboardingStep.SETTINGS_CHECK;
} else {
nextStep = OnboardingStep.USER_LEVEL_SELECT;
}
}
// Always go to mode selection
nextStep = OnboardingStep.MODE_SELECT;
break;
case OnboardingStep.MODE_SELECT:
// This step is now integrated into INTRO, but keep for backward compatibility
if (this.state.uiMode === 'fluent') {
nextStep = OnboardingStep.FLUENT_PLACEMENT;
nextStep = OnboardingStep.FLUENT_OVERVIEW;
} else {
// Legacy mode: check for existing changes
if (this.state.userHasChanges) {
nextStep = OnboardingStep.SETTINGS_CHECK;
} else {
nextStep = OnboardingStep.USER_LEVEL_SELECT;
}
}
break;
case OnboardingStep.FLUENT_PLACEMENT:
nextStep = OnboardingStep.FLUENT_COMPONENTS;
break;
case OnboardingStep.FLUENT_COMPONENTS:
// Check if user has changes
if (this.state.userHasChanges) {
nextStep = OnboardingStep.SETTINGS_CHECK;
} else {
nextStep = OnboardingStep.USER_LEVEL_SELECT;
}
break;
case OnboardingStep.FLUENT_OVERVIEW:
nextStep = OnboardingStep.FLUENT_WS_SELECTOR;
break;
case OnboardingStep.FLUENT_WS_SELECTOR:
nextStep = OnboardingStep.FLUENT_MAIN_NAV;
break;
case OnboardingStep.FLUENT_MAIN_NAV:
nextStep = OnboardingStep.FLUENT_PROJECTS;
break;
case OnboardingStep.FLUENT_PROJECTS:
nextStep = OnboardingStep.FLUENT_OTHER_VIEWS;
break;
case OnboardingStep.FLUENT_OTHER_VIEWS:
nextStep = OnboardingStep.FLUENT_TOPNAV;
break;
case OnboardingStep.FLUENT_TOPNAV:
nextStep = OnboardingStep.SETTINGS_CHECK;
break;
case OnboardingStep.SETTINGS_CHECK:
// User decided to continue wizard
if (this.state.settingsCheckAction === 'wizard') {
@ -238,18 +234,29 @@ export class OnboardingController {
prevStep = OnboardingStep.INTRO;
break;
case OnboardingStep.FLUENT_PLACEMENT:
case OnboardingStep.FLUENT_OVERVIEW:
prevStep = OnboardingStep.MODE_SELECT;
break;
case OnboardingStep.FLUENT_COMPONENTS:
prevStep = OnboardingStep.MODE_SELECT;
case OnboardingStep.FLUENT_WS_SELECTOR:
prevStep = OnboardingStep.FLUENT_OVERVIEW;
break;
case OnboardingStep.FLUENT_MAIN_NAV:
prevStep = OnboardingStep.FLUENT_WS_SELECTOR;
break;
case OnboardingStep.FLUENT_PROJECTS:
prevStep = OnboardingStep.FLUENT_MAIN_NAV;
break;
case OnboardingStep.FLUENT_OTHER_VIEWS:
prevStep = OnboardingStep.FLUENT_PROJECTS;
break;
case OnboardingStep.FLUENT_TOPNAV:
prevStep = OnboardingStep.FLUENT_OTHER_VIEWS;
break;
case OnboardingStep.SETTINGS_CHECK:
// Go back to components or mode select based on UI mode
// Go back to last fluent step or mode select based on UI mode
if (this.state.uiMode === 'fluent') {
prevStep = OnboardingStep.FLUENT_COMPONENTS;
prevStep = OnboardingStep.FLUENT_TOPNAV;
} else {
prevStep = OnboardingStep.MODE_SELECT;
}
@ -260,7 +267,7 @@ export class OnboardingController {
if (this.state.userHasChanges && this.state.settingsCheckAction === 'wizard') {
prevStep = OnboardingStep.SETTINGS_CHECK;
} else if (this.state.uiMode === 'fluent') {
prevStep = OnboardingStep.FLUENT_COMPONENTS;
prevStep = OnboardingStep.FLUENT_TOPNAV;
} else {
prevStep = OnboardingStep.MODE_SELECT;
}
@ -333,8 +340,12 @@ export class OnboardingController {
// Must have a config selected
return !!this.state.selectedConfig;
case OnboardingStep.FLUENT_PLACEMENT:
case OnboardingStep.FLUENT_COMPONENTS:
case OnboardingStep.FLUENT_OVERVIEW:
case OnboardingStep.FLUENT_WS_SELECTOR:
case OnboardingStep.FLUENT_MAIN_NAV:
case OnboardingStep.FLUENT_PROJECTS:
case OnboardingStep.FLUENT_OTHER_VIEWS:
case OnboardingStep.FLUENT_TOPNAV:
case OnboardingStep.FILE_FILTER:
case OnboardingStep.CONFIG_PREVIEW:
case OnboardingStep.TASK_CREATION_GUIDE:

View file

@ -9,8 +9,12 @@ import { OnboardingLayout } from "./OnboardingLayout";
// Import step components
import { IntroStep } from "./steps/IntroStep";
import { ModeSelectionStep } from "./steps/ModeSelectionStep";
import { PlacementStep } from "./steps/PlacementStep";
import { FluentComponentsStep } from "./steps/FluentComponentsStep";
import { FluentOverviewStep } from "./steps/FluentOverviewStep";
import { FluentWorkspaceSelectorStep } from "./steps/FluentWorkspaceSelectorStep";
import { FluentMainNavigationStep } from "./steps/FluentMainNavigationStep";
import { FluentProjectSectionStep } from "./steps/FluentProjectSectionStep";
import { FluentOtherViewsStep } from "./steps/FluentOtherViewsStep";
import { FluentTopNavigationStep } from "./steps/FluentTopNavigationStep";
import { UserLevelStep } from "./steps/UserLevelStep";
import { FileFilterStep } from "./steps/FileFilterStep";
import { ConfigPreviewStep } from "./steps/ConfigPreviewStep";
@ -147,12 +151,23 @@ export class OnboardingView extends ItemView {
ModeSelectionStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_PLACEMENT:
PlacementStep.render(headerEl, contentEl, this.controller);
case OnboardingStep.FLUENT_OVERVIEW:
FluentOverviewStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_COMPONENTS:
FluentComponentsStep.render(headerEl, contentEl, this.controller);
case OnboardingStep.FLUENT_WS_SELECTOR:
FluentWorkspaceSelectorStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_MAIN_NAV:
FluentMainNavigationStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_PROJECTS:
FluentProjectSectionStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_OTHER_VIEWS:
FluentOtherViewsStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.FLUENT_TOPNAV:
FluentTopNavigationStep.render(headerEl, contentEl, this.controller);
break;
case OnboardingStep.SETTINGS_CHECK:
@ -217,11 +232,20 @@ export class OnboardingView extends ItemView {
console.log("handleNext - UI Mode:", state.uiMode);
console.log("handleNext - User has changes:", state.userHasChanges);
// Special handling for INTRO step - show config check transition
if (step === OnboardingStep.INTRO && state.uiMode !== "fluent") {
// If user has changes, show checking animation before settings check
// Show config check transition only when entering Settings Check from:
// - Mode Select with Legacy
// - The last Fluent step (Top Navigation)
if (step === OnboardingStep.MODE_SELECT) {
if (state.uiMode === "legacy" && state.userHasChanges) {
// Clear header before showing transition to avoid residual UI
this.layout.clearHeader();
await this.showConfigCheckTransition();
}
}
if (step === OnboardingStep.FLUENT_TOPNAV) {
if (state.userHasChanges) {
console.log("Showing config check transition from INTRO");
// Clear header before showing transition to avoid residual UI
this.layout.clearHeader();
await this.showConfigCheckTransition();
}
}

View file

@ -42,7 +42,7 @@ export class ComponentPreviewFactory {
const content = sidebar.createDiv({ cls: "v2-sidebar-content" });
// Primary navigation section
const primarySection = content.createDiv({ cls: "v2-sidebar-section" });
const primarySection = content.createDiv({ cls: "v2-sidebar-section v2-sidebar-section-primary" });
const primaryList = primarySection.createDiv({ cls: "v2-navigation-list" });
const primaryItems = [
@ -55,7 +55,7 @@ export class ComponentPreviewFactory {
primaryItems.forEach((item, index) => {
const navItem = primaryList.createDiv({
cls: "v2-navigation-item",
attr: { "data-view-id": item.id }
attr: { "data-view-id": item.id, tabindex: "0", role: "button" }
});
if (index === 0) navItem.addClass("is-active");
@ -68,7 +68,7 @@ export class ComponentPreviewFactory {
});
// Projects section
const projectsSection = content.createDiv({ cls: "v2-sidebar-section" });
const projectsSection = content.createDiv({ cls: "v2-sidebar-section v2-sidebar-section-projects" });
const projectsHeader = projectsSection.createDiv({ cls: "v2-section-header" });
projectsHeader.createSpan({ text: t("Projects") });
@ -100,7 +100,7 @@ export class ComponentPreviewFactory {
projects.forEach(project => {
const projectItem = scrollArea.createDiv({
cls: "v2-project-item",
attr: { "data-project-id": project.id, "data-level": "0" }
attr: { "data-project-id": project.id, "data-level": "0", tabindex: "0", role: "button" }
});
const colorDot = projectItem.createDiv({ cls: "v2-project-color" });
colorDot.style.backgroundColor = project.color;
@ -109,7 +109,7 @@ export class ComponentPreviewFactory {
});
// Other views section
const otherSection = content.createDiv({ cls: "v2-sidebar-section" });
const otherSection = content.createDiv({ cls: "v2-sidebar-section v2-sidebar-section-other" });
const otherHeader = otherSection.createDiv({ cls: "v2-section-header" });
otherHeader.createSpan({ text: t("Other Views") });
@ -123,12 +123,36 @@ export class ComponentPreviewFactory {
otherItems.forEach(item => {
const navItem = otherList.createDiv({
cls: "v2-navigation-item",
attr: { "data-view-id": item.id }
attr: { "data-view-id": item.id, tabindex: "0", role: "button" }
});
const icon = navItem.createDiv({ cls: "v2-navigation-icon" });
setIcon(icon, item.icon);
navItem.createSpan({ text: item.label, cls: "v2-navigation-label" });
});
// Interactive preview: simple selection toggle (visual only)
const root = container.querySelector(".v2-sidebar")!;
const handleActivate = (target: HTMLElement) => {
const item = target.closest<HTMLElement>(".v2-navigation-item, .v2-project-item");
if (!item) return;
const parentList = item.parentElement;
if (!parentList) return;
// Clear previous active in the same group
Array.from(parentList.children).forEach((el) => el.classList.remove("is-active"));
item.classList.add("is-active");
};
root.addEventListener("click", (e) => {
handleActivate(e.target as HTMLElement);
});
root.addEventListener("keydown", (e: KeyboardEvent) => {
if (e.key === "Enter" || e.key === " ") {
const target = e.target as HTMLElement;
handleActivate(target);
// Prevent page scroll on Space
e.preventDefault();
}
});
}
/**

View file

@ -0,0 +1,46 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentMainNavigationStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Main Navigation") });
headerEl.createEl("p", {
text: t("Access Inbox, Today, Upcoming and more from the primary section"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview focus-mode" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
ComponentPreviewFactory.createSidebarPreview(preview);
const primary = preview.querySelector<HTMLElement>(".v2-sidebar-section-primary");
primary?.classList.add("is-focused");
const dimTargets = preview.querySelectorAll<HTMLElement>(
".v2-sidebar-section-projects, .v2-sidebar-section-other"
);
dimTargets.forEach((el) => el.classList.add("is-dimmed"));
desc.createEl("h3", { text: t("Navigate core views") });
desc.createEl("p", {
text: t("Quickly jump to core views like Inbox, Today, Upcoming and Flagged."),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Unread counts and indicators keep you informed"),
t("Keyboard-ready with clear selection states"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -0,0 +1,48 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentOtherViewsStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Other Views") });
headerEl.createEl("p", {
text: t("Access Calendar, Gantt and Tags from the other views section"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview focus-mode" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
ComponentPreviewFactory.createSidebarPreview(preview);
const other = preview.querySelector<HTMLElement>(".v2-sidebar-section-other");
other?.classList.add("is-focused");
const dimTargets = preview.querySelectorAll<HTMLElement>(
".v2-sidebar-section-primary, .v2-sidebar-section-projects"
);
dimTargets.forEach((el) => el.classList.add("is-dimmed"));
desc.createEl("h3", { text: t("Specialized views") });
desc.createEl("p", {
text: t(
"Quickly reach Calendar scheduling, Gantt timelines and tag-based filtering."
),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Compact list with clear icons"),
t("Consistent selection feedback"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -0,0 +1,47 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentOverviewStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Discover Fluent Interface") });
headerEl.createEl("p", {
text: t("A quick overview of the Fluent layout and its key areas"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
// Build a compact overview: Sidebar + Top Navigation
const sidebarWrap = preview.createDiv();
ComponentPreviewFactory.createSidebarPreview(sidebarWrap);
const topNavWrap = preview.createDiv();
(topNavWrap as HTMLElement).style.marginTop = "16px";
ComponentPreviewFactory.createTopNavigationPreview(topNavWrap);
desc.createEl("h3", { text: t("Fluent Layout Overview") });
desc.createEl("p", {
text: t(
"Fluent groups navigation on the left and global tools at the top, keeping your content area clean and focused."
),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Sidebar for switching views and managing projects"),
t("Top navigation for search, view modes and quick settings"),
t("Consistent styling with clear hierarchy and feedback"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -0,0 +1,49 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentProjectSectionStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Projects Section") });
headerEl.createEl("p", {
text: t("Organize your work with projects and hierarchies"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview focus-mode" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
ComponentPreviewFactory.createSidebarPreview(preview);
const projects = preview.querySelector<HTMLElement>(".v2-sidebar-section-projects");
projects?.classList.add("is-focused");
const dimTargets = preview.querySelectorAll<HTMLElement>(
".v2-sidebar-section-primary, .v2-sidebar-section-other"
);
dimTargets.forEach((el) => el.classList.add("is-dimmed"));
desc.createEl("h3", { text: t("Project organization") });
desc.createEl("p", {
text: t(
"Group related tasks under projects. Build nested hierarchies and get quick stats."
),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Color-coded projects with counts"),
t("Supports nested structures for complex work"),
t("Quick actions via project popover (preview)"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -0,0 +1,43 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentTopNavigationStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Top Navigation") });
headerEl.createEl("p", {
text: t("Search, switch views, and access quick settings from the top bar"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview focus-mode" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
ComponentPreviewFactory.createTopNavigationPreview(preview);
const topNav = preview.querySelector<HTMLElement>(".v2-top-navigation");
topNav?.classList.add("is-focused");
desc.createEl("h3", { text: t("Global controls") });
desc.createEl("p", {
text: t(
"Use the top bar to search across everything, switch view modes, and open notifications or settings."
),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Quick view mode switching"),
t("Accessible controls with clear icons"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -0,0 +1,50 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { ComponentPreviewFactory } from "../previews/ComponentPreviewFactory";
import "@/styles/onboarding-components.css";
export class FluentWorkspaceSelectorStep {
static render(
headerEl: HTMLElement,
contentEl: HTMLElement,
controller: OnboardingController
) {
headerEl.empty();
contentEl.empty();
headerEl.createEl("h1", { text: t("Workspace Selector") });
headerEl.createEl("p", {
text: t("Switch and manage workspaces from the top of the sidebar"),
cls: "onboarding-subtitle",
});
const showcase = contentEl.createDiv({ cls: "component-showcase" });
const preview = showcase.createDiv({ cls: "component-showcase-preview focus-mode" });
const desc = showcase.createDiv({ cls: "component-showcase-description" });
ComponentPreviewFactory.createSidebarPreview(preview);
// Focus workspace selector, dim other parts
const wsBtn = preview.querySelector<HTMLElement>(".workspace-selector-button");
wsBtn?.classList.add("is-focused");
const contentSections = preview.querySelectorAll<HTMLElement>(".v2-sidebar-content, .v2-navigation-list, .v2-project-list, .v2-section-header, .v2-top-navigation");
contentSections.forEach((el) => {
if (!wsBtn || !el.contains(wsBtn)) el.classList.add("is-dimmed");
});
desc.createEl("h3", { text: t("Manage your spaces") });
desc.createEl("p", {
text: t(
"Use the workspace selector to switch between personal, work, or any custom workspace."
),
});
const ul = desc.createEl("ul", { cls: "component-feature-list" });
[
t("Quickly toggle between multiple workspaces"),
t("Create and organize workspaces for different contexts"),
t("Consistent placement at the top of the sidebar"),
].forEach((txt) => ul.createEl("li", { text: txt }));
}
}

View file

@ -1,8 +1,7 @@
import { t } from "@/translations/helper";
import { OnboardingController } from "../OnboardingController";
import { OnboardingController, OnboardingStep } from "../OnboardingController";
import { TypingAnimation } from "./intro/TypingAnimation";
import { TransitionMessage } from "./intro/TransitionMessage";
import { ModeSelectionStep, UIMode } from "./ModeSelectionStep";
/**
* Intro Step - Welcome message with typing animation + mode selection
@ -73,21 +72,9 @@ export class IntroStep {
// Start typing animation
new TypingAnimation(typingContainer, messages, () => {
// After typing completes, show mode selection in same container
const modeContainer = introWrapper.createDiv({
cls: "intro-mode-selection-container"
});
// Render mode selection inline (without clearing intro-line-4)
ModeSelectionStep.renderInline(
modeContainer,
controller,
(mode: UIMode) => {
// User selected a mode, show footer with Next button
controller.setUIMode(mode);
footerEl.style.display = "";
}
);
// Typing completed: show footer and move to Mode Selection step
footerEl.style.display = "";
controller.setStep(OnboardingStep.MODE_SELECT);
});
}

View file

@ -21,6 +21,15 @@ export class ModeSelectionStep {
headerEl.empty();
contentEl.empty();
// Intro guidance text (same as intro-line-4)
headerEl.createEl("p", {
cls: "intro-line intro-line-4",
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?"
),
});
// Get current state
const currentMode = controller.getState().uiMode;

View file

@ -33,6 +33,11 @@
overflow: hidden;
position: relative;
border: 1px solid var(--background-modifier-border);
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
}
.component-showcase-description {
@ -223,4 +228,39 @@
.component-showcase-preview.tg-v2-container.component-preview-sidebar .v2-sidebar {
width: 100%;
}
}
/* Preview interactivity (scoped to preview only) */
.component-showcase-preview .component-preview-sidebar .v2-navigation-item,
.component-showcase-preview .component-preview-sidebar .v2-project-item {
pointer-events: auto;
cursor: pointer;
outline: none;
}
.component-showcase-preview .component-preview-sidebar .v2-navigation-item.is-active,
.component-showcase-preview .component-preview-sidebar .v2-project-item.is-active {
background: var(--background-modifier-hover);
border-radius: var(--radius-s);
}
.component-showcase-preview .component-preview-sidebar .v2-navigation-item:focus-visible,
.component-showcase-preview .component-preview-sidebar .v2-project-item:focus-visible {
box-shadow: 0 0 0 2px var(--interactive-accent);
border-radius: var(--radius-s);
}
/* Focus/Dim mode for step highlights */
.component-showcase-preview.focus-mode .is-dimmed {
opacity: 0.5;
filter: grayscale(15%);
pointer-events: none;
}
.component-showcase-preview.focus-mode .is-focused {
position: relative;
outline: 1px solid var(--color-accent);
outline-offset: -1px;
sborder-radius: var(--radius-s);
z-index: 1;
}

View file

@ -1499,6 +1499,7 @@
color: var(--text-muted);
line-height: 1.8;
margin-bottom: var(--size-4-5);
text-align: left;s
}
/* AI-style streaming characters */

File diff suppressed because one or more lines are too long