From 12aee016eec15f3a16d206c91abc4ce9e85ee10a Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Sat, 18 Jul 2026 19:18:57 -0700 Subject: [PATCH] FN-8352: promote ideation to top-level navigation Make ideation independently accessible from desktop and mobile navigation. - Add an Ideation view backed by the Command Center panel and view-state routing. - Surface Ideation in desktop/sidebar and mobile primary navigation, with coverage for navigation and rendering. - Document the experimental setting and add a minor changeset. Files changed: .changeset/fn-8352-ideation-top-level-view.md | 7 ++++ docs/dashboard-guide.md | 14 +++++++ docs/settings-reference.md | 3 +- .../src/__tests__/mobile-nav-primary-items.test.ts | 10 ++++- packages/core/src/mobile-nav-primary-items.ts | 17 +++++++- packages/dashboard/app/App.tsx | 16 ++++++- packages/dashboard/app/components/Header.tsx | 21 +++++++-- .../dashboard/app/components/LeftSidebarNav.tsx | 8 +++- packages/dashboard/app/components/MobileNavBar.tsx | 2 + .../dashboard/app/components/SettingsModal.tsx | 6 +++ .../app/components/__tests__/App.test.tsx | 20 +++++++++ .../app/components/__tests__/Header.test.tsx | 15 +++++++ .../components/__tests__/LeftSidebarNav.test.tsx | 5 +++ .../app/components/__tests__/MobileNavBar.test.tsx | 43 +++++++++++++++--- .../components/command-center/CommandCenter.tsx | 12 +++--- .../__tests__/CommandCenter.test.tsx | 7 ++-- .../__tests__/IdeationPanel.test.tsx | 49 ++++++++++++++++++++++ .../app/components/dashboard/MainContent.tsx | 13 ++++++ .../dashboard/app/components/dashboard/types.ts | 1 + .../settings/sections/GeneralSection.tsx | 10 ++--- packages/dashboard/app/hooks/useViewState.ts | 8 +++- 21 files changed, 256 insertions(+), 31 deletions(-) Fusion-Task-Id: FN-8352 Fusion-Task-Lineage: e7791119-2fe5-4e68-b669-0e5bd8df9b49 Co-authored-by: Fusion (runfusion.ai) --- .changeset/fn-8352-ideation-top-level-view.md | 7 +++ docs/dashboard-guide.md | 14 ++++++ docs/settings-reference.md | 3 +- .../mobile-nav-primary-items.test.ts | 10 +++- packages/core/src/mobile-nav-primary-items.ts | 17 ++++++- packages/dashboard/app/App.tsx | 16 +++++- packages/dashboard/app/components/Header.tsx | 21 ++++++-- .../app/components/LeftSidebarNav.tsx | 8 ++- .../dashboard/app/components/MobileNavBar.tsx | 2 + .../app/components/SettingsModal.tsx | 6 +++ .../app/components/__tests__/App.test.tsx | 20 ++++++++ .../app/components/__tests__/Header.test.tsx | 15 ++++++ .../__tests__/LeftSidebarNav.test.tsx | 5 ++ .../__tests__/MobileNavBar.test.tsx | 43 +++++++++++++--- .../command-center/CommandCenter.tsx | 12 ++--- .../__tests__/CommandCenter.test.tsx | 7 +-- .../__tests__/IdeationPanel.test.tsx | 49 +++++++++++++++++++ .../app/components/dashboard/MainContent.tsx | 13 +++++ .../app/components/dashboard/types.ts | 1 + .../settings/sections/GeneralSection.tsx | 10 ++-- packages/dashboard/app/hooks/useViewState.ts | 8 ++- 21 files changed, 256 insertions(+), 31 deletions(-) create mode 100644 .changeset/fn-8352-ideation-top-level-view.md create mode 100644 packages/dashboard/app/components/command-center/__tests__/IdeationPanel.test.tsx diff --git a/.changeset/fn-8352-ideation-top-level-view.md b/.changeset/fn-8352-ideation-top-level-view.md new file mode 100644 index 0000000000..d1d79e0d98 --- /dev/null +++ b/.changeset/fn-8352-ideation-top-level-view.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": minor +--- + +summary: Ideation is now a top-level experimental sidebar/mobile view instead of a Command Center tab. +category: feature +dev: `experimentalFeatures.ideationView` is default-off; mobile keeps Ideation More-only, redirects disabled deep links to Board, and removes the Command Center tab. diff --git a/docs/dashboard-guide.md b/docs/dashboard-guide.md index e76d62f38c..786ac5a464 100644 --- a/docs/dashboard-guide.md +++ b/docs/dashboard-guide.md @@ -1024,6 +1024,20 @@ Navigation: For the full research workflow, provider setup, CLI commands, API reference, and agent integration, see the canonical [Research guide](./research.md). +## Ideation View + + +Ideation is a standalone dashboard surface for capturing divergent candidates and converging one into the Mission hierarchy. + +> Available only when `experimentalFeatures.ideationView` is enabled. The flag is off by default and is managed in **Settings → Experimental Features**. + +Navigation: +- Desktop/tablet: **Left sidebar → Ideation**, or the Header **More views** menu when the desktop sidebar is opted out +- Mobile: **More** sheet → **Ideation** +- Mobile footer customization never promotes Ideation to a primary tab, including when older persisted settings list it + +The view replaces the former **Command Center → Ideation** tab. It preserves the same empty, populated, converged, and request-error states, so sessions and Mission handoff evidence remain in one canonical surface. + ## Files Modal The Files modal provides a workspace-aware file browser and editor. diff --git a/docs/settings-reference.md b/docs/settings-reference.md index 37b36e344e..4dc6d9c965 100644 --- a/docs/settings-reference.md +++ b/docs/settings-reference.md @@ -165,7 +165,7 @@ Fusion automatically falls back to ntfy's JSON publish format when a notificatio | `researchGlobalMaxSearchResults` | `number` | `undefined` | Maximum search results per provider query. | | `researchGlobalFetchTimeoutMs` | `number` | `30000` | Timeout for individual HTTP fetches in milliseconds. | | `researchGlobalUserAgent` | `string` | `"FusionResearchBot/1.0"` | User-Agent header for HTTP requests made by research providers. | -| `experimentalFeatures` | `Record` | `{}` | Global-scoped experimental feature flags. Includes `experimentalFeatures.researchView`, which gates all Research surfaces and tools (dashboard view, engine task-session tools, and CLI `fn_research_*` tools), and `experimentalFeatures.evalsView`, which gates Evals surfaces (dashboard view, Settings → Scheduled Evals, and scheduled-eval cron execution). | +| `experimentalFeatures` | `Record` | `{}` | Global-scoped experimental feature flags. Includes `experimentalFeatures.researchView`, which gates all Research surfaces and tools (dashboard view, engine task-session tools, and CLI `fn_research_*` tools); `experimentalFeatures.evalsView`, which gates Evals surfaces (dashboard view, Settings → Scheduled Evals, and scheduled-eval cron execution); and default-off `experimentalFeatures.ideationView`, which gates the top-level Ideation view (desktop sidebar/Header fallback and mobile More only). | | `remoteAccess` | `RemoteAccessSettings` | `{ activeProvider: null, providers: {...}, tokenStrategy: {...}, lifecycle: {...} }` | Global-scoped remote access provider + token strategy configuration used by Remote Access routes and tunnel lifecycle controls. | | `mcpServers` | `McpServersSettings` | `{ enabled: false, servers: [] }` | Global MCP server declarations shared across projects. Project `mcpServers` can enable/disable the effective set, override a same-named global server, or disable a global server with a same-named `enabled:false` entry. Sensitive env/header/token values must be `{ secretRef, scope }` references to Fusion-managed secrets, never plaintext. | | `worktrunk` | `WorktrunkSettings` | `{ enabled: false, binaryPath: undefined, installedBinaryPath: undefined, onFailure: "fail" }` | Global defaults for worktrunk integration. Merged field-by-field with project `worktrunk` values; project values override global values for matching fields. | @@ -1567,6 +1567,7 @@ Common built-in dashboard/runtime flags include: - `todoView` (enables dashboard Todo View; see [Todo View](./todo-view.md)) - `researchView` - `evalsView` (gates Evals dashboard view, Settings → Scheduled Evals section, and scheduled-eval cron execution) +- `ideationView` (default off; gates the top-level Ideation view, which is mobile More-only and replaces the Command Center Ideation tab) - `workflowGraphExecutor` (enables the workflow-IR interpreter path) - `graphNativePostMerge` (**default-ON**; the graph is the sole owner of post-merge `optional-group` steps after a successful merge — the legacy merger-owned post-merge path was deleted. Post-merge failures are non-blocking. See [Workflow Steps → Execution Phases](./workflow-steps.md#execution-phases)) - `workflowInterpreterDualObserve` (retired/inert; stale persisted `true` values are forced OFF and must not reactivate hidden shadow observation) diff --git a/packages/core/src/__tests__/mobile-nav-primary-items.test.ts b/packages/core/src/__tests__/mobile-nav-primary-items.test.ts index c5d9848fe6..efdcd76eb2 100644 --- a/packages/core/src/__tests__/mobile-nav-primary-items.test.ts +++ b/packages/core/src/__tests__/mobile-nav-primary-items.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { DEFAULT_MOBILE_NAV_PRIMARY_ITEMS, resolveMobileNavPrimaryItems } from "../mobile-nav-primary-items.js"; +import { DEFAULT_MOBILE_NAV_PRIMARY_ITEMS, MOBILE_NAV_SELECTABLE_ITEMS, resolveMobileNavPrimaryItems } from "../mobile-nav-primary-items.js"; describe("resolveMobileNavPrimaryItems", () => { it("uses the existing six-tab order for unset or empty values", () => { @@ -14,6 +14,14 @@ describe("resolveMobileNavPrimaryItems", () => { expect(resolved.omittedItems).toContain("settings"); }); + it("keeps Ideation in More when stale settings try to promote it", () => { + const resolved = resolveMobileNavPrimaryItems({ mobileNavPrimaryItems: ["ideation"] }); + + expect(MOBILE_NAV_SELECTABLE_ITEMS).toContain("ideation"); + expect(resolved.primaryItems).not.toContain("ideation"); + expect(resolved.omittedItems).toContain("ideation"); + }); + it("keeps newly eligible settings and documents, drops overflow-only ids, deduplicates, and clamps footer tabs", () => { const resolved = resolveMobileNavPrimaryItems({ mobileNavPrimaryItems: ["settings", "tasks", "more", "documents", "tasks", "agents", "missions", "chat", "mailbox", "planning", "unknown"], diff --git a/packages/core/src/mobile-nav-primary-items.ts b/packages/core/src/mobile-nav-primary-items.ts index 63aeaf645d..dbb17e8c09 100644 --- a/packages/core/src/mobile-nav-primary-items.ts +++ b/packages/core/src/mobile-nav-primary-items.ts @@ -1,6 +1,6 @@ import type { ProjectSettings } from "./types.js"; -/** The only destinations that may be promoted into the mobile footer. */ +/** Destinations represented by the mobile navigation registry. */ export const MOBILE_NAV_SELECTABLE_ITEMS = [ "command-center", "tasks", @@ -25,6 +25,13 @@ export const MOBILE_NAV_SELECTABLE_ITEMS = [ "memory", "research", "evals", + /* + FNXC:Navigation 2026-08-01-00:00: + FN-8352 makes Ideation a top-level experimental destination, but it remains + More-only on mobile. Keep it registered for labels and More rendering while + excluding it from the footer-promotion registry below. + */ + "ideation", "goals", "todos", "dev-server", @@ -57,11 +64,17 @@ export const MOBILE_NAV_SELECTABLE_ITEM_LABEL_KEYS: Record => item !== "ideation", +); + export const DEFAULT_MOBILE_NAV_PRIMARY_ITEMS: MobileNavSelectableItem[] = [ "command-center", "tasks", "agents", "missions", "chat", "mailbox", ]; @@ -81,7 +94,7 @@ the render layer suppresses disabled experimental destinations, and More remains */ export function resolveMobileNavPrimaryItems(settings?: Pick): ResolvedMobileNavPrimaryItems { const selected = Array.isArray(settings?.mobileNavPrimaryItems) ? settings.mobileNavPrimaryItems : []; - const valid = new Set(MOBILE_NAV_SELECTABLE_ITEMS); + const valid = new Set(MOBILE_NAV_PRIMARY_SELECTABLE_ITEMS); const primaryItems = selected.reduce((items, id) => { if (valid.has(id) && !items.includes(id as MobileNavSelectableItem) && items.length < MAX_MOBILE_NAV_PRIMARY_ITEMS) { items.push(id as MobileNavSelectableItem); diff --git a/packages/dashboard/app/App.tsx b/packages/dashboard/app/App.tsx index d4346be26c..3f71b8dcbe 100644 --- a/packages/dashboard/app/App.tsx +++ b/packages/dashboard/app/App.tsx @@ -801,6 +801,7 @@ function AppInner() { const nodesEnabled = experimentalFeatures.nodesView === true; const researchEnabled = experimentalFeatures.researchView === true; const evalsEnabled = experimentalFeatures.evalsView === true; + const ideationEnabled = experimentalFeatures.ideationView === true; /* FNXC:QuickAddSubtaskFlag 2026-06-21-00:00: Missing or false `subtaskBreakdown` settings must hide the AI Subtask quick-add handoff across List, Board, and New Task Modal surfaces; only an explicit true wires the callback. */ const subtaskBreakdownEnabled = experimentalFeatures.subtaskBreakdown === true; /* @@ -863,13 +864,22 @@ function AppInner() { if (taskView === "evals" && !evalsEnabled) { handleChangeTaskView("board"); } + /* + FNXC:Navigation 2026-08-01-00:00: + FN-8352 promotes Ideation to a default-off experimental top-level view. + Redirect persisted and deep-linked disabled views to Board so MainContent + never leaves users on a blank unavailable surface. + */ + if (taskView === "ideation" && !ideationEnabled) { + handleChangeTaskView("board"); + } if (taskView === "goalsView" && !goalsEnabled) { handleChangeTaskView("board"); } if (taskView === "todos" && !todosEnabled) { handleChangeTaskView("board"); } - }, [taskView, settingsLoaded, skillsEnabled, insightsEnabled, handleChangeTaskView, agentsEnabled, memoryEnabled, devServerEnabled, researchEnabled, evalsEnabled, goalsEnabled, todosEnabled, graphPluginTaskView]); + }, [taskView, settingsLoaded, skillsEnabled, insightsEnabled, handleChangeTaskView, agentsEnabled, memoryEnabled, devServerEnabled, researchEnabled, evalsEnabled, ideationEnabled, goalsEnabled, todosEnabled, graphPluginTaskView]); const { availableModels, @@ -1518,6 +1528,7 @@ function AppInner() { openSettingsWithNav, researchReadinessVersion, evalsEnabled, + ideationEnabled, memoryEnabled, goalsEnabled, handleOpenMission, @@ -1729,6 +1740,7 @@ function AppInner() { devServerView: devServerEnabled, researchView: researchEnabled, evalsView: evalsEnabled, + ideationView: ideationEnabled, goalsView: goalsEnabled, leftSidebarNav: leftSidebarNavEnabled, rightDock: rightDockEnabled, @@ -1763,6 +1775,7 @@ function AppInner() { devServerView: devServerEnabled, researchView: researchEnabled, evalsView: evalsEnabled, + ideationView: ideationEnabled, goalsView: goalsEnabled, }} pluginDashboardViews={pluginDashboardViews} @@ -1850,6 +1863,7 @@ function AppInner() { todoView: todosEnabled, researchView: researchEnabled, evalsView: evalsEnabled, + ideationView: ideationEnabled, goalsView: goalsEnabled, }} pluginDashboardViews={pluginDashboardViews} diff --git a/packages/dashboard/app/components/Header.tsx b/packages/dashboard/app/components/Header.tsx index bf564b2a9e..4a4e84031c 100644 --- a/packages/dashboard/app/components/Header.tsx +++ b/packages/dashboard/app/components/Header.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useRef, useCallback, useMemo, type ReactNode } from "react"; import { useTranslation } from "react-i18next"; -import { Settings, LayoutGrid, List, Search, X, Activity, MoreHorizontal, Clock, Folder, History, GitBranch, Monitor, Workflow, Bot, Target, Grid3X3, Mail, MessageSquare, Check, Zap, Sparkles, FileText, Brain, CheckSquare, Lock, Gauge, ChevronDown, ChevronRight, PanelRight } from "lucide-react"; +import { Settings, LayoutGrid, List, Search, X, Activity, MoreHorizontal, Clock, Folder, History, GitBranch, Monitor, Workflow, Bot, Target, Grid3X3, Mail, MessageSquare, Check, Zap, Sparkles, FileText, Brain, CheckSquare, Lock, Gauge, Lightbulb, ChevronDown, ChevronRight, PanelRight } from "lucide-react"; import "./Header.css"; // ProjectSelector styles used by the imported standalone component. import "./ProjectSelector.css"; @@ -132,7 +132,7 @@ export interface HeaderProps { /** Whether the current view is a remote node */ isRemote?: boolean; /** Experimental feature flags controlling visibility of nav items. */ - experimentalFeatures?: { insights?: boolean; memoryView?: boolean; devServer?: boolean; devServerView?: boolean; researchView?: boolean; evalsView?: boolean; goalsView?: boolean; leftSidebarNav?: boolean; rightDock?: boolean }; + experimentalFeatures?: { insights?: boolean; memoryView?: boolean; devServer?: boolean; devServerView?: boolean; researchView?: boolean; evalsView?: boolean; ideationView?: boolean; goalsView?: boolean; leftSidebarNav?: boolean; rightDock?: boolean }; pluginDashboardViews?: PluginDashboardViewEntry[]; shellConnectionControl?: ReactNode; } @@ -236,6 +236,7 @@ export function Header({ return !!( onChangeView || experimentalFeatures?.researchView || + experimentalFeatures?.ideationView || todosEnabled || experimentalFeatures?.insights || @@ -784,7 +785,7 @@ export function Header({ <> )} + {experimentalFeatures?.ideationView && ( + + )} {experimentalFeatures?.insights && (