@qontinui/navigation 0.1.7 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -75,6 +75,7 @@ __export(index_exports, {
75
75
  isGroupExpanded: () => isGroupExpanded,
76
76
  isItemActive: () => isItemActive,
77
77
  isItemAvailable: () => isItemAvailable,
78
+ isItemDemoted: () => isItemDemoted,
78
79
  isItemExpanded: () => isItemExpanded,
79
80
  isSecondaryOpenFor: () => isSecondaryOpenFor,
80
81
  isValidIconName: () => isValidIconName,
@@ -106,7 +107,13 @@ var WORKSPACE_ITEMS = [
106
107
  description: "Tell the runner what to do in plain English",
107
108
  route: "/prompt-home",
108
109
  color: "#8B5CF6",
109
- productMode: "ai"
110
+ productMode: "ai",
111
+ // Runner-only demotion. On the runner this page COMPOSES a loop workflow
112
+ // (setup → verification → agentic → completion) from a prompt and hands it
113
+ // to the executor — it is the loop paradigm's front door, not the
114
+ // Terminal's, so it belongs behind the advanced disclosure there. On
115
+ // qontinui-web the same route is the app's landing page, so it stays.
116
+ hidden: ["runner"]
110
117
  },
111
118
  {
112
119
  id: "gui-automation",
@@ -115,7 +122,10 @@ var WORKSPACE_ITEMS = [
115
122
  description: "Run and schedule workflows",
116
123
  route: "/execute",
117
124
  color: "#10B981",
118
- productMode: "visual"
125
+ productMode: "visual",
126
+ // Web demotes it (coord+sessions default); the runner reaches it through
127
+ // the Visual product mode, which is itself disclosure-gated there.
128
+ hidden: ["web"]
119
129
  },
120
130
  {
121
131
  id: "terminal",
@@ -134,7 +144,12 @@ var WORKSPACE_ITEMS = [
134
144
  description: "Monitor active executions",
135
145
  route: "/runs/active",
136
146
  color: "#4A90D9",
137
- productMode: "ai"
147
+ productMode: "ai",
148
+ // The loop-workflow cockpit: phase badge, iteration counter, GUI/Playwright
149
+ // widgets and the "did the fix work?" verification panel. A Terminal
150
+ // session watches itself in the Terminal, so this is advanced on both
151
+ // platforms (web already demoted it locally).
152
+ hidden: true
138
153
  },
139
154
  {
140
155
  id: "productivity",
@@ -167,7 +182,9 @@ var SESSION_ITEMS = [
167
182
  icon: "Zap",
168
183
  description: "Action execution log",
169
184
  route: "/runs/actions",
170
- color: "#4A90D9"
185
+ color: "#4A90D9",
186
+ // GUI action log — only ever populated by a visual/loop workflow run.
187
+ hidden: true
171
188
  },
172
189
  {
173
190
  id: "run-image",
@@ -175,7 +192,8 @@ var SESSION_ITEMS = [
175
192
  icon: "Image",
176
193
  description: "Visual recognition results",
177
194
  route: "/runs/image-recognition",
178
- color: "#4A90D9"
195
+ color: "#4A90D9",
196
+ hidden: true
179
197
  },
180
198
  {
181
199
  id: "run-findings",
@@ -191,7 +209,9 @@ var SESSION_ITEMS = [
191
209
  icon: "FileSearch",
192
210
  description: "State exploration results",
193
211
  route: "/runs/state-exploration",
194
- color: "#4A90D9"
212
+ color: "#4A90D9",
213
+ // State-machine exploration is a visual-GUI-automation artifact.
214
+ hidden: true
195
215
  },
196
216
  {
197
217
  id: "run-tests",
@@ -199,7 +219,9 @@ var SESSION_ITEMS = [
199
219
  icon: "TestTube",
200
220
  description: "Playwright test results",
201
221
  route: "/runs/test-results",
202
- color: "#4A90D9"
222
+ color: "#4A90D9",
223
+ // Populated by the loop's verification phase, never by a chat session.
224
+ hidden: true
203
225
  },
204
226
  {
205
227
  id: "run-ai-output",
@@ -244,10 +266,21 @@ var REVIEW_ITEMS = [
244
266
  icon: "History",
245
267
  description: "Browse and manage all runs",
246
268
  hasChildren: true,
269
+ // Runs is a parent WITH a page of its own (/runs — the run browser).
270
+ // `selectsFirstChild: false` alone means "expand only, activate nothing",
271
+ // which left the item inert: clicking Runs opened the flyout but never
272
+ // dispatched a tab change. It must activate its OWN id, not its first
273
+ // child's ("run-recap"), so `selectsFirstChild` cannot express this.
247
274
  selectsFirstChild: false,
275
+ hasOwnPage: true,
248
276
  route: "/runs",
249
277
  color: "#4A90D9",
250
- productMode: "ai"
278
+ productMode: "ai",
279
+ // Runner keeps it: a Terminal/Claude session creates a `task_run`
280
+ // (`workflow_type: "chat"`), so this IS the runner's session history.
281
+ // qontinui-web demotes it — its sessions live in `coord.sessions` and
282
+ // produce no task_runs.
283
+ hidden: ["web"]
251
284
  },
252
285
  {
253
286
  id: "run-findings",
@@ -256,15 +289,25 @@ var REVIEW_ITEMS = [
256
289
  description: "Review findings and HITL questions across runs",
257
290
  route: "/runs/findings",
258
291
  color: "#4A90D9",
259
- productMode: "ai"
292
+ productMode: "ai",
293
+ hidden: ["web"]
260
294
  },
261
295
  {
262
- id: "memory",
296
+ // Id is "observations" (NOT "memory"): it must match the runner's
297
+ // `MainTabId` union member / `TabContent` case that actually renders this
298
+ // page. The old "memory" id was in no consumer's tab union, so clicking
299
+ // the item activated a tab that did not exist.
300
+ id: "observations",
263
301
  label: "Memory",
264
302
  icon: "Brain",
265
303
  description: "Cross-session observation memory from past runs",
266
304
  route: "/observe/memory",
267
305
  color: "#8B5CF6",
306
+ // Runner-only: qontinui-web has no `observe/` route tree at all, so this
307
+ // item's route 404s there — and because qontinui-web derives its co-pilot
308
+ // page map from getWebNavigation(), an un-gated item also advertises that
309
+ // 404 as a navigable target to the planner. Gate it at the source.
310
+ platforms: ["runner"],
268
311
  productMode: "ai"
269
312
  },
270
313
  {
@@ -443,8 +486,11 @@ var BUILD_ITEMS = [
443
486
  route: "/vga",
444
487
  color: "var(--brand-secondary)",
445
488
  // Visual GUI automation belongs to Visual mode, not AI Dev (removed from
446
- // the AI Dev sidebar on both runner and web).
447
- productMode: "visual"
489
+ // the AI Dev sidebar on both runner and web). Web demotes it outright; on
490
+ // the runner it is reached through the Visual product mode, which is itself
491
+ // behind the "Show visual GUI automation" disclosure.
492
+ productMode: "visual",
493
+ hidden: ["web"]
448
494
  },
449
495
  {
450
496
  id: "orchestration-loop",
@@ -645,7 +691,11 @@ var CONFIGURE_ITEMS = [
645
691
  icon: "Radio",
646
692
  description: "Workflow event bus, queue status, circuit breaker",
647
693
  hiddenInProd: true,
648
- productMode: "ai"
694
+ productMode: "ai",
695
+ // Workflow event bus — a loop-workflow internal. Dev-only AND advanced, so
696
+ // the CONFIGURE group has no default-visible item left and the group header
697
+ // is dropped entirely by filterGroupsForPlatform.
698
+ hidden: true
649
699
  }
650
700
  ];
651
701
  var CONFIGURE_GROUP = {
@@ -1089,11 +1139,16 @@ function setShowHiddenItems(show) {
1089
1139
  function getShowHiddenItems() {
1090
1140
  return _showHiddenItems;
1091
1141
  }
1142
+ function isItemDemoted(item, platform) {
1143
+ const demoted = item.hidden;
1144
+ if (!demoted) return false;
1145
+ return demoted === true || demoted.includes(platform);
1146
+ }
1092
1147
  function isItemAvailable(item, platform) {
1093
1148
  if (item.hiddenInProd && !isDevelopmentMode()) {
1094
1149
  return false;
1095
1150
  }
1096
- if (item.hidden && !_showHiddenItems) {
1151
+ if (isItemDemoted(item, platform) && !_showHiddenItems) {
1097
1152
  return false;
1098
1153
  }
1099
1154
  if (_productMode && item.productMode && item.productMode !== "both" && item.productMode !== _productMode) {
@@ -1439,6 +1494,7 @@ function NavigationItemShell({
1439
1494
  isGroupExpanded,
1440
1495
  isItemActive,
1441
1496
  isItemAvailable,
1497
+ isItemDemoted,
1442
1498
  isItemExpanded,
1443
1499
  isSecondaryOpenFor,
1444
1500
  isValidIconName,
package/dist/index.d.cts CHANGED
@@ -30,6 +30,25 @@ interface NavigationItem {
30
30
  hasChildren?: boolean;
31
31
  /** If true, clicking this item selects its first child instead of itself */
32
32
  selectsFirstChild?: boolean;
33
+ /**
34
+ * If true, this item is a parent (`hasChildren: true`) that ALSO has a page
35
+ * of its own: clicking it must activate the item's own id (in addition to
36
+ * opening its children flyout/submenu), not just expand.
37
+ *
38
+ * This is the third case in the parent-click contract, and it exists because
39
+ * the other two cannot express it:
40
+ * - `selectsFirstChild: true` — activates the FIRST CHILD's id (e.g.
41
+ * "settings" → "settings-account"). The parent has no page of its own.
42
+ * - `selectsFirstChild: false` — activates NOTHING; the click only
43
+ * expands. Correct for a pure container.
44
+ * - `hasOwnPage: true` — activates the PARENT's own id (e.g.
45
+ * "runs" → the Runs page). Use when the parent is a real destination.
46
+ *
47
+ * Consumers implementing item-click MUST honour this: when a parent has
48
+ * `hasOwnPage`, dispatch the tab/route change for `item.id` itself.
49
+ * Ignored for items without children.
50
+ */
51
+ hasOwnPage?: boolean;
33
52
  /** Platform availability - if not set, available on all platforms */
34
53
  platforms?: Platform[];
35
54
  /** Badge count or status to show on this item */
@@ -46,10 +65,18 @@ interface NavigationItem {
46
65
  * sidebar until the user opts in via the "Show advanced automation features"
47
66
  * setting (wired through `setShowHiddenItems(true)`). The route/tab id is
48
67
  * still registered, so deep-links and programmatic tab-activation continue
49
- * to resolve. Used to demote the workflow-authoring surfaces (workflow
50
- * builder, DAG editor, orchestration loop, step builders) now that the
51
- * Terminal is the primary entry point. */
52
- hidden?: boolean;
68
+ * to resolve. Used to demote the verification-agentic loop-workflow surfaces
69
+ * (workflow builder, DAG editor, orchestration loop, step builders, the
70
+ * run/execution dashboards) now that the Terminal is the primary entry point.
71
+ *
72
+ * `true` demotes the item on EVERY platform. A platform list demotes it only
73
+ * on the named platforms — the runner and qontinui-web disagree about a
74
+ * handful of items (the runner's Home/prompt entry is advanced there but is
75
+ * still web's landing page; web's coord+sessions default drops runs/findings
76
+ * the runner keeps), and expressing that at the source beats each app
77
+ * carrying its own parallel demotion list that silently drifts.
78
+ */
79
+ hidden?: boolean | Platform[];
53
80
  /** Product mode visibility - "ai", "visual", or "both" (default: shown in all modes) */
54
81
  productMode?: "ai" | "visual" | "both";
55
82
  /** URL path for web routing (e.g., "/build/workflows") */
@@ -158,10 +185,12 @@ type NavigationAction = {
158
185
  * - platform: "runner" | "web" | both (default) — which app shows the item
159
186
  * - productMode: "ai" | "visual" | "both" | undefined (default=both) — which product mode
160
187
  * - hiddenInProd: true — dev-only items hidden in production
161
- * - hidden: true — "advanced" surfaces (the workflow-authoring tools) kept
162
- * out of the default sidebar until the user opts in via "Show advanced
163
- * automation features" (setShowHiddenItems). Route/tab id stays registered,
164
- * so deep-links (e.g. the Terminal "save as workflow" disclosure) resolve.
188
+ * - hidden: true | Platform[] — "advanced" surfaces (the loop-workflow and
189
+ * workflow-authoring tools) kept out of the default sidebar until the user
190
+ * opts in via "Show advanced automation features" (setShowHiddenItems).
191
+ * Route/tab id stays registered, so deep-links (e.g. the Terminal
192
+ * "save as workflow" disclosure) resolve. A platform list demotes on only
193
+ * those platforms — see `NavigationItem.hidden`.
165
194
  *
166
195
  * ---------------------------------------------------------------------------
167
196
  * Terminal-centric information architecture (2026-06).
@@ -184,16 +213,30 @@ type NavigationAction = {
184
213
  * (Groups that end up with no visible items are dropped by
185
214
  * filterGroupsForPlatform, so no bare group header renders.)
186
215
  *
216
+ * 2026-07 follow-up — the loop-workflow残り. The first pass demoted the
217
+ * BUILDERS but left the loop's RUNTIME surfaces in the default set: the
218
+ * natural-language Home prompt (which composes and launches a
219
+ * setup→verification→agentic→completion workflow), the Active dashboard
220
+ * (phase badge, iteration counter, "did the fix work?" verification widget),
221
+ * and the GUI/loop-specific run detail tabs (Actions, Image Recognition,
222
+ * State Explorer, Test Results). Those are the loop paradigm's cockpit, not
223
+ * the Terminal's, so they are demoted too. What stays under Runs is the set a
224
+ * Terminal session actually produces — chat sessions create `task_runs` with
225
+ * `workflow_type: "chat"`, so Summary / Findings / AI Output / Statistics /
226
+ * AI Data remain first-class.
227
+ *
187
228
  * The default (toggle OFF, AI Dev mode) is therefore the lean, Terminal-first
188
- * set: WORKSPACE (Home / Terminal / Active / Productivity), REVIEW (Runs /
189
- * Findings / Memory / Knowledge), and SYSTEM (Settings / Help). This mirrors
229
+ * set: WORKSPACE (Terminal / Productivity), REVIEW (Runs / Findings / Memory /
230
+ * Knowledge / Helper Tasks), and SYSTEM (Settings / Help). This mirrors
190
231
  * qontinui-web's coord+sessions-centric default menu.
191
232
  *
192
233
  * Both platforms share this structure; per-item `platforms`/`productMode`
193
234
  * filters yield the right view for each (web has no Terminal; runner has no
194
235
  * Dashboard/Runners, etc.). qontinui-web additionally demotes a few items the
195
- * runner keeps (runs/active/findings/memory — task_run-scoped, not session-
196
- * scoped) via its own local list; those stay un-`hidden` here.
236
+ * runner keeps (runs/active/findings — task_run-scoped, not session-scoped)
237
+ * via its own local list; those stay un-`hidden` here. The observation-memory
238
+ * item is no longer among them: it is `platforms: ["runner"]` now, since web
239
+ * has no `observe/` route tree (its route 404s there).
197
240
  */
198
241
 
199
242
  declare const WORKSPACE_ITEMS: NavigationItem[];
@@ -299,6 +342,13 @@ declare function setShowHiddenItems(show: boolean): void;
299
342
  * Whether `hidden` items are currently shown.
300
343
  */
301
344
  declare function getShowHiddenItems(): boolean;
345
+ /**
346
+ * Whether an item's `hidden` ("advanced") demotion applies on this platform.
347
+ *
348
+ * `hidden: true` demotes everywhere; `hidden: ["runner"]` demotes only on the
349
+ * runner. Absent/false is never demoted.
350
+ */
351
+ declare function isItemDemoted(item: NavigationItem, platform: Platform): boolean;
302
352
  /**
303
353
  * Filter a navigation item based on platform.
304
354
  * Returns true if the item should be shown on the given platform.
@@ -477,4 +527,4 @@ interface NavigationItemShellProps {
477
527
  */
478
528
  declare function NavigationItemShell({ item, onActivate, children, }: NavigationItemShellProps): React.ReactElement;
479
529
 
480
- export { AUTOMATE_GROUP, AUTOMATE_ITEMS, BUILD_GROUP, BUILD_ITEMS, CHILDREN_MAP, CONFIGURE_GROUP, CONFIGURE_ITEMS, DEV_GROUP, DEV_ITEMS, ICON_NAMES, INSIGHTS_GROUP, INSIGHTS_ITEMS, type IconName, NAVIGATION_GROUPS, type NavigationAction, type NavigationBadge, type NavigationGroup, type NavigationItem, type NavigationItemLike, NavigationItemShell, type NavigationItemShellProps, type NavigationState, type Platform, REVIEW_GROUP, REVIEW_ITEMS, RUNS_ITEMS, SESSION_ITEMS, SETTINGS_ITEMS, SPEND_GROUP, SPEND_ITEMS, STORAGE_KEYS, SYSTEM_GROUP, SYSTEM_ITEMS, type SecondarySidebarState, WORKSPACE_GROUP, WORKSPACE_ITEMS, createInitialState, deserializeState, filterGroupForPlatform, filterGroupsForPlatform, filterItemsForPlatform, findItemById, getAllItems, getChildrenForPlatform, getChildrenItems, getItemGroup, getNavigationGroups, getProductMode, getRunnerNavigation, getShowHiddenItems, getWebNavigation, isDevelopmentMode, isGroupExpanded, isItemActive, isItemAvailable, isItemExpanded, isSecondaryOpenFor, isValidIconName, navigationActions, navigationReducer, serializeState, setDevelopmentMode, setProductMode, setShowHiddenItems, useNavigationItem };
530
+ export { AUTOMATE_GROUP, AUTOMATE_ITEMS, BUILD_GROUP, BUILD_ITEMS, CHILDREN_MAP, CONFIGURE_GROUP, CONFIGURE_ITEMS, DEV_GROUP, DEV_ITEMS, ICON_NAMES, INSIGHTS_GROUP, INSIGHTS_ITEMS, type IconName, NAVIGATION_GROUPS, type NavigationAction, type NavigationBadge, type NavigationGroup, type NavigationItem, type NavigationItemLike, NavigationItemShell, type NavigationItemShellProps, type NavigationState, type Platform, REVIEW_GROUP, REVIEW_ITEMS, RUNS_ITEMS, SESSION_ITEMS, SETTINGS_ITEMS, SPEND_GROUP, SPEND_ITEMS, STORAGE_KEYS, SYSTEM_GROUP, SYSTEM_ITEMS, type SecondarySidebarState, WORKSPACE_GROUP, WORKSPACE_ITEMS, createInitialState, deserializeState, filterGroupForPlatform, filterGroupsForPlatform, filterItemsForPlatform, findItemById, getAllItems, getChildrenForPlatform, getChildrenItems, getItemGroup, getNavigationGroups, getProductMode, getRunnerNavigation, getShowHiddenItems, getWebNavigation, isDevelopmentMode, isGroupExpanded, isItemActive, isItemAvailable, isItemDemoted, isItemExpanded, isSecondaryOpenFor, isValidIconName, navigationActions, navigationReducer, serializeState, setDevelopmentMode, setProductMode, setShowHiddenItems, useNavigationItem };
package/dist/index.d.ts CHANGED
@@ -30,6 +30,25 @@ interface NavigationItem {
30
30
  hasChildren?: boolean;
31
31
  /** If true, clicking this item selects its first child instead of itself */
32
32
  selectsFirstChild?: boolean;
33
+ /**
34
+ * If true, this item is a parent (`hasChildren: true`) that ALSO has a page
35
+ * of its own: clicking it must activate the item's own id (in addition to
36
+ * opening its children flyout/submenu), not just expand.
37
+ *
38
+ * This is the third case in the parent-click contract, and it exists because
39
+ * the other two cannot express it:
40
+ * - `selectsFirstChild: true` — activates the FIRST CHILD's id (e.g.
41
+ * "settings" → "settings-account"). The parent has no page of its own.
42
+ * - `selectsFirstChild: false` — activates NOTHING; the click only
43
+ * expands. Correct for a pure container.
44
+ * - `hasOwnPage: true` — activates the PARENT's own id (e.g.
45
+ * "runs" → the Runs page). Use when the parent is a real destination.
46
+ *
47
+ * Consumers implementing item-click MUST honour this: when a parent has
48
+ * `hasOwnPage`, dispatch the tab/route change for `item.id` itself.
49
+ * Ignored for items without children.
50
+ */
51
+ hasOwnPage?: boolean;
33
52
  /** Platform availability - if not set, available on all platforms */
34
53
  platforms?: Platform[];
35
54
  /** Badge count or status to show on this item */
@@ -46,10 +65,18 @@ interface NavigationItem {
46
65
  * sidebar until the user opts in via the "Show advanced automation features"
47
66
  * setting (wired through `setShowHiddenItems(true)`). The route/tab id is
48
67
  * still registered, so deep-links and programmatic tab-activation continue
49
- * to resolve. Used to demote the workflow-authoring surfaces (workflow
50
- * builder, DAG editor, orchestration loop, step builders) now that the
51
- * Terminal is the primary entry point. */
52
- hidden?: boolean;
68
+ * to resolve. Used to demote the verification-agentic loop-workflow surfaces
69
+ * (workflow builder, DAG editor, orchestration loop, step builders, the
70
+ * run/execution dashboards) now that the Terminal is the primary entry point.
71
+ *
72
+ * `true` demotes the item on EVERY platform. A platform list demotes it only
73
+ * on the named platforms — the runner and qontinui-web disagree about a
74
+ * handful of items (the runner's Home/prompt entry is advanced there but is
75
+ * still web's landing page; web's coord+sessions default drops runs/findings
76
+ * the runner keeps), and expressing that at the source beats each app
77
+ * carrying its own parallel demotion list that silently drifts.
78
+ */
79
+ hidden?: boolean | Platform[];
53
80
  /** Product mode visibility - "ai", "visual", or "both" (default: shown in all modes) */
54
81
  productMode?: "ai" | "visual" | "both";
55
82
  /** URL path for web routing (e.g., "/build/workflows") */
@@ -158,10 +185,12 @@ type NavigationAction = {
158
185
  * - platform: "runner" | "web" | both (default) — which app shows the item
159
186
  * - productMode: "ai" | "visual" | "both" | undefined (default=both) — which product mode
160
187
  * - hiddenInProd: true — dev-only items hidden in production
161
- * - hidden: true — "advanced" surfaces (the workflow-authoring tools) kept
162
- * out of the default sidebar until the user opts in via "Show advanced
163
- * automation features" (setShowHiddenItems). Route/tab id stays registered,
164
- * so deep-links (e.g. the Terminal "save as workflow" disclosure) resolve.
188
+ * - hidden: true | Platform[] — "advanced" surfaces (the loop-workflow and
189
+ * workflow-authoring tools) kept out of the default sidebar until the user
190
+ * opts in via "Show advanced automation features" (setShowHiddenItems).
191
+ * Route/tab id stays registered, so deep-links (e.g. the Terminal
192
+ * "save as workflow" disclosure) resolve. A platform list demotes on only
193
+ * those platforms — see `NavigationItem.hidden`.
165
194
  *
166
195
  * ---------------------------------------------------------------------------
167
196
  * Terminal-centric information architecture (2026-06).
@@ -184,16 +213,30 @@ type NavigationAction = {
184
213
  * (Groups that end up with no visible items are dropped by
185
214
  * filterGroupsForPlatform, so no bare group header renders.)
186
215
  *
216
+ * 2026-07 follow-up — the loop-workflow残り. The first pass demoted the
217
+ * BUILDERS but left the loop's RUNTIME surfaces in the default set: the
218
+ * natural-language Home prompt (which composes and launches a
219
+ * setup→verification→agentic→completion workflow), the Active dashboard
220
+ * (phase badge, iteration counter, "did the fix work?" verification widget),
221
+ * and the GUI/loop-specific run detail tabs (Actions, Image Recognition,
222
+ * State Explorer, Test Results). Those are the loop paradigm's cockpit, not
223
+ * the Terminal's, so they are demoted too. What stays under Runs is the set a
224
+ * Terminal session actually produces — chat sessions create `task_runs` with
225
+ * `workflow_type: "chat"`, so Summary / Findings / AI Output / Statistics /
226
+ * AI Data remain first-class.
227
+ *
187
228
  * The default (toggle OFF, AI Dev mode) is therefore the lean, Terminal-first
188
- * set: WORKSPACE (Home / Terminal / Active / Productivity), REVIEW (Runs /
189
- * Findings / Memory / Knowledge), and SYSTEM (Settings / Help). This mirrors
229
+ * set: WORKSPACE (Terminal / Productivity), REVIEW (Runs / Findings / Memory /
230
+ * Knowledge / Helper Tasks), and SYSTEM (Settings / Help). This mirrors
190
231
  * qontinui-web's coord+sessions-centric default menu.
191
232
  *
192
233
  * Both platforms share this structure; per-item `platforms`/`productMode`
193
234
  * filters yield the right view for each (web has no Terminal; runner has no
194
235
  * Dashboard/Runners, etc.). qontinui-web additionally demotes a few items the
195
- * runner keeps (runs/active/findings/memory — task_run-scoped, not session-
196
- * scoped) via its own local list; those stay un-`hidden` here.
236
+ * runner keeps (runs/active/findings — task_run-scoped, not session-scoped)
237
+ * via its own local list; those stay un-`hidden` here. The observation-memory
238
+ * item is no longer among them: it is `platforms: ["runner"]` now, since web
239
+ * has no `observe/` route tree (its route 404s there).
197
240
  */
198
241
 
199
242
  declare const WORKSPACE_ITEMS: NavigationItem[];
@@ -299,6 +342,13 @@ declare function setShowHiddenItems(show: boolean): void;
299
342
  * Whether `hidden` items are currently shown.
300
343
  */
301
344
  declare function getShowHiddenItems(): boolean;
345
+ /**
346
+ * Whether an item's `hidden` ("advanced") demotion applies on this platform.
347
+ *
348
+ * `hidden: true` demotes everywhere; `hidden: ["runner"]` demotes only on the
349
+ * runner. Absent/false is never demoted.
350
+ */
351
+ declare function isItemDemoted(item: NavigationItem, platform: Platform): boolean;
302
352
  /**
303
353
  * Filter a navigation item based on platform.
304
354
  * Returns true if the item should be shown on the given platform.
@@ -477,4 +527,4 @@ interface NavigationItemShellProps {
477
527
  */
478
528
  declare function NavigationItemShell({ item, onActivate, children, }: NavigationItemShellProps): React.ReactElement;
479
529
 
480
- export { AUTOMATE_GROUP, AUTOMATE_ITEMS, BUILD_GROUP, BUILD_ITEMS, CHILDREN_MAP, CONFIGURE_GROUP, CONFIGURE_ITEMS, DEV_GROUP, DEV_ITEMS, ICON_NAMES, INSIGHTS_GROUP, INSIGHTS_ITEMS, type IconName, NAVIGATION_GROUPS, type NavigationAction, type NavigationBadge, type NavigationGroup, type NavigationItem, type NavigationItemLike, NavigationItemShell, type NavigationItemShellProps, type NavigationState, type Platform, REVIEW_GROUP, REVIEW_ITEMS, RUNS_ITEMS, SESSION_ITEMS, SETTINGS_ITEMS, SPEND_GROUP, SPEND_ITEMS, STORAGE_KEYS, SYSTEM_GROUP, SYSTEM_ITEMS, type SecondarySidebarState, WORKSPACE_GROUP, WORKSPACE_ITEMS, createInitialState, deserializeState, filterGroupForPlatform, filterGroupsForPlatform, filterItemsForPlatform, findItemById, getAllItems, getChildrenForPlatform, getChildrenItems, getItemGroup, getNavigationGroups, getProductMode, getRunnerNavigation, getShowHiddenItems, getWebNavigation, isDevelopmentMode, isGroupExpanded, isItemActive, isItemAvailable, isItemExpanded, isSecondaryOpenFor, isValidIconName, navigationActions, navigationReducer, serializeState, setDevelopmentMode, setProductMode, setShowHiddenItems, useNavigationItem };
530
+ export { AUTOMATE_GROUP, AUTOMATE_ITEMS, BUILD_GROUP, BUILD_ITEMS, CHILDREN_MAP, CONFIGURE_GROUP, CONFIGURE_ITEMS, DEV_GROUP, DEV_ITEMS, ICON_NAMES, INSIGHTS_GROUP, INSIGHTS_ITEMS, type IconName, NAVIGATION_GROUPS, type NavigationAction, type NavigationBadge, type NavigationGroup, type NavigationItem, type NavigationItemLike, NavigationItemShell, type NavigationItemShellProps, type NavigationState, type Platform, REVIEW_GROUP, REVIEW_ITEMS, RUNS_ITEMS, SESSION_ITEMS, SETTINGS_ITEMS, SPEND_GROUP, SPEND_ITEMS, STORAGE_KEYS, SYSTEM_GROUP, SYSTEM_ITEMS, type SecondarySidebarState, WORKSPACE_GROUP, WORKSPACE_ITEMS, createInitialState, deserializeState, filterGroupForPlatform, filterGroupsForPlatform, filterItemsForPlatform, findItemById, getAllItems, getChildrenForPlatform, getChildrenItems, getItemGroup, getNavigationGroups, getProductMode, getRunnerNavigation, getShowHiddenItems, getWebNavigation, isDevelopmentMode, isGroupExpanded, isItemActive, isItemAvailable, isItemDemoted, isItemExpanded, isSecondaryOpenFor, isValidIconName, navigationActions, navigationReducer, serializeState, setDevelopmentMode, setProductMode, setShowHiddenItems, useNavigationItem };
package/dist/index.js CHANGED
@@ -16,7 +16,13 @@ var WORKSPACE_ITEMS = [
16
16
  description: "Tell the runner what to do in plain English",
17
17
  route: "/prompt-home",
18
18
  color: "#8B5CF6",
19
- productMode: "ai"
19
+ productMode: "ai",
20
+ // Runner-only demotion. On the runner this page COMPOSES a loop workflow
21
+ // (setup → verification → agentic → completion) from a prompt and hands it
22
+ // to the executor — it is the loop paradigm's front door, not the
23
+ // Terminal's, so it belongs behind the advanced disclosure there. On
24
+ // qontinui-web the same route is the app's landing page, so it stays.
25
+ hidden: ["runner"]
20
26
  },
21
27
  {
22
28
  id: "gui-automation",
@@ -25,7 +31,10 @@ var WORKSPACE_ITEMS = [
25
31
  description: "Run and schedule workflows",
26
32
  route: "/execute",
27
33
  color: "#10B981",
28
- productMode: "visual"
34
+ productMode: "visual",
35
+ // Web demotes it (coord+sessions default); the runner reaches it through
36
+ // the Visual product mode, which is itself disclosure-gated there.
37
+ hidden: ["web"]
29
38
  },
30
39
  {
31
40
  id: "terminal",
@@ -44,7 +53,12 @@ var WORKSPACE_ITEMS = [
44
53
  description: "Monitor active executions",
45
54
  route: "/runs/active",
46
55
  color: "#4A90D9",
47
- productMode: "ai"
56
+ productMode: "ai",
57
+ // The loop-workflow cockpit: phase badge, iteration counter, GUI/Playwright
58
+ // widgets and the "did the fix work?" verification panel. A Terminal
59
+ // session watches itself in the Terminal, so this is advanced on both
60
+ // platforms (web already demoted it locally).
61
+ hidden: true
48
62
  },
49
63
  {
50
64
  id: "productivity",
@@ -77,7 +91,9 @@ var SESSION_ITEMS = [
77
91
  icon: "Zap",
78
92
  description: "Action execution log",
79
93
  route: "/runs/actions",
80
- color: "#4A90D9"
94
+ color: "#4A90D9",
95
+ // GUI action log — only ever populated by a visual/loop workflow run.
96
+ hidden: true
81
97
  },
82
98
  {
83
99
  id: "run-image",
@@ -85,7 +101,8 @@ var SESSION_ITEMS = [
85
101
  icon: "Image",
86
102
  description: "Visual recognition results",
87
103
  route: "/runs/image-recognition",
88
- color: "#4A90D9"
104
+ color: "#4A90D9",
105
+ hidden: true
89
106
  },
90
107
  {
91
108
  id: "run-findings",
@@ -101,7 +118,9 @@ var SESSION_ITEMS = [
101
118
  icon: "FileSearch",
102
119
  description: "State exploration results",
103
120
  route: "/runs/state-exploration",
104
- color: "#4A90D9"
121
+ color: "#4A90D9",
122
+ // State-machine exploration is a visual-GUI-automation artifact.
123
+ hidden: true
105
124
  },
106
125
  {
107
126
  id: "run-tests",
@@ -109,7 +128,9 @@ var SESSION_ITEMS = [
109
128
  icon: "TestTube",
110
129
  description: "Playwright test results",
111
130
  route: "/runs/test-results",
112
- color: "#4A90D9"
131
+ color: "#4A90D9",
132
+ // Populated by the loop's verification phase, never by a chat session.
133
+ hidden: true
113
134
  },
114
135
  {
115
136
  id: "run-ai-output",
@@ -154,10 +175,21 @@ var REVIEW_ITEMS = [
154
175
  icon: "History",
155
176
  description: "Browse and manage all runs",
156
177
  hasChildren: true,
178
+ // Runs is a parent WITH a page of its own (/runs — the run browser).
179
+ // `selectsFirstChild: false` alone means "expand only, activate nothing",
180
+ // which left the item inert: clicking Runs opened the flyout but never
181
+ // dispatched a tab change. It must activate its OWN id, not its first
182
+ // child's ("run-recap"), so `selectsFirstChild` cannot express this.
157
183
  selectsFirstChild: false,
184
+ hasOwnPage: true,
158
185
  route: "/runs",
159
186
  color: "#4A90D9",
160
- productMode: "ai"
187
+ productMode: "ai",
188
+ // Runner keeps it: a Terminal/Claude session creates a `task_run`
189
+ // (`workflow_type: "chat"`), so this IS the runner's session history.
190
+ // qontinui-web demotes it — its sessions live in `coord.sessions` and
191
+ // produce no task_runs.
192
+ hidden: ["web"]
161
193
  },
162
194
  {
163
195
  id: "run-findings",
@@ -166,15 +198,25 @@ var REVIEW_ITEMS = [
166
198
  description: "Review findings and HITL questions across runs",
167
199
  route: "/runs/findings",
168
200
  color: "#4A90D9",
169
- productMode: "ai"
201
+ productMode: "ai",
202
+ hidden: ["web"]
170
203
  },
171
204
  {
172
- id: "memory",
205
+ // Id is "observations" (NOT "memory"): it must match the runner's
206
+ // `MainTabId` union member / `TabContent` case that actually renders this
207
+ // page. The old "memory" id was in no consumer's tab union, so clicking
208
+ // the item activated a tab that did not exist.
209
+ id: "observations",
173
210
  label: "Memory",
174
211
  icon: "Brain",
175
212
  description: "Cross-session observation memory from past runs",
176
213
  route: "/observe/memory",
177
214
  color: "#8B5CF6",
215
+ // Runner-only: qontinui-web has no `observe/` route tree at all, so this
216
+ // item's route 404s there — and because qontinui-web derives its co-pilot
217
+ // page map from getWebNavigation(), an un-gated item also advertises that
218
+ // 404 as a navigable target to the planner. Gate it at the source.
219
+ platforms: ["runner"],
178
220
  productMode: "ai"
179
221
  },
180
222
  {
@@ -353,8 +395,11 @@ var BUILD_ITEMS = [
353
395
  route: "/vga",
354
396
  color: "var(--brand-secondary)",
355
397
  // Visual GUI automation belongs to Visual mode, not AI Dev (removed from
356
- // the AI Dev sidebar on both runner and web).
357
- productMode: "visual"
398
+ // the AI Dev sidebar on both runner and web). Web demotes it outright; on
399
+ // the runner it is reached through the Visual product mode, which is itself
400
+ // behind the "Show visual GUI automation" disclosure.
401
+ productMode: "visual",
402
+ hidden: ["web"]
358
403
  },
359
404
  {
360
405
  id: "orchestration-loop",
@@ -555,7 +600,11 @@ var CONFIGURE_ITEMS = [
555
600
  icon: "Radio",
556
601
  description: "Workflow event bus, queue status, circuit breaker",
557
602
  hiddenInProd: true,
558
- productMode: "ai"
603
+ productMode: "ai",
604
+ // Workflow event bus — a loop-workflow internal. Dev-only AND advanced, so
605
+ // the CONFIGURE group has no default-visible item left and the group header
606
+ // is dropped entirely by filterGroupsForPlatform.
607
+ hidden: true
559
608
  }
560
609
  ];
561
610
  var CONFIGURE_GROUP = {
@@ -999,11 +1048,16 @@ function setShowHiddenItems(show) {
999
1048
  function getShowHiddenItems() {
1000
1049
  return _showHiddenItems;
1001
1050
  }
1051
+ function isItemDemoted(item, platform) {
1052
+ const demoted = item.hidden;
1053
+ if (!demoted) return false;
1054
+ return demoted === true || demoted.includes(platform);
1055
+ }
1002
1056
  function isItemAvailable(item, platform) {
1003
1057
  if (item.hiddenInProd && !isDevelopmentMode()) {
1004
1058
  return false;
1005
1059
  }
1006
- if (item.hidden && !_showHiddenItems) {
1060
+ if (isItemDemoted(item, platform) && !_showHiddenItems) {
1007
1061
  return false;
1008
1062
  }
1009
1063
  if (_productMode && item.productMode && item.productMode !== "both" && item.productMode !== _productMode) {
@@ -1348,6 +1402,7 @@ export {
1348
1402
  isGroupExpanded,
1349
1403
  isItemActive,
1350
1404
  isItemAvailable,
1405
+ isItemDemoted,
1351
1406
  isItemExpanded,
1352
1407
  isSecondaryOpenFor,
1353
1408
  isValidIconName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qontinui/navigation",
3
- "version": "0.1.7",
3
+ "version": "0.3.0",
4
4
  "description": "Shared navigation structure for Qontinui applications",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -13,6 +13,7 @@
13
13
  "build": "tsup src/index.ts --format cjs,esm --dts && node -e \"if(!require('fs').statSync('dist/index.d.ts').size)process.exit(1)\"",
14
14
  "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
15
15
  "typecheck": "tsc --noEmit",
16
+ "test": "vitest run",
16
17
  "lint": "eslint src/",
17
18
  "clean": "rm -rf dist",
18
19
  "prepublishOnly": "npm run build"
@@ -56,6 +57,7 @@
56
57
  "react": "^19.0.0",
57
58
  "tsup": "^8.0.1",
58
59
  "typescript": "^6.0.2",
59
- "typescript-eslint": "^8.61.0"
60
+ "typescript-eslint": "^8.61.0",
61
+ "vitest": "^3.2.7"
60
62
  }
61
63
  }