@plmbr/notebook-intelligence 5.0.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/LICENSE +674 -0
- package/README.md +412 -0
- package/lib/api.d.ts +288 -0
- package/lib/api.js +927 -0
- package/lib/cell-output-bundle.d.ts +25 -0
- package/lib/cell-output-bundle.js +129 -0
- package/lib/cell-output-toolbar.d.ts +26 -0
- package/lib/cell-output-toolbar.js +188 -0
- package/lib/chat-progress-feedback.d.ts +3 -0
- package/lib/chat-progress-feedback.js +27 -0
- package/lib/chat-sidebar.d.ts +92 -0
- package/lib/chat-sidebar.js +3452 -0
- package/lib/command-ids.d.ts +39 -0
- package/lib/command-ids.js +44 -0
- package/lib/components/ask-user-question.d.ts +2 -0
- package/lib/components/ask-user-question.js +85 -0
- package/lib/components/checkbox.d.ts +2 -0
- package/lib/components/checkbox.js +30 -0
- package/lib/components/claude-mcp-panel.d.ts +2 -0
- package/lib/components/claude-mcp-panel.js +275 -0
- package/lib/components/claude-mcp-paste.d.ts +7 -0
- package/lib/components/claude-mcp-paste.js +104 -0
- package/lib/components/claude-session-picker.d.ts +8 -0
- package/lib/components/claude-session-picker.js +127 -0
- package/lib/components/form-dialog.d.ts +25 -0
- package/lib/components/form-dialog.js +35 -0
- package/lib/components/launcher-picker.d.ts +6 -0
- package/lib/components/launcher-picker.js +135 -0
- package/lib/components/mcp-util.d.ts +2 -0
- package/lib/components/mcp-util.js +37 -0
- package/lib/components/notebook-generation-popover.d.ts +7 -0
- package/lib/components/notebook-generation-popover.js +60 -0
- package/lib/components/pill.d.ts +2 -0
- package/lib/components/pill.js +5 -0
- package/lib/components/plugins-panel.d.ts +3 -0
- package/lib/components/plugins-panel.js +466 -0
- package/lib/components/settings-panel.d.ts +11 -0
- package/lib/components/settings-panel.js +742 -0
- package/lib/components/skills-panel.d.ts +2 -0
- package/lib/components/skills-panel.js +1264 -0
- package/lib/handler.d.ts +8 -0
- package/lib/handler.js +36 -0
- package/lib/icons.d.ts +45 -0
- package/lib/icons.js +54 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +2079 -0
- package/lib/markdown-renderer.d.ts +10 -0
- package/lib/markdown-renderer.js +64 -0
- package/lib/notebook-generation-toolbar.d.ts +16 -0
- package/lib/notebook-generation-toolbar.js +197 -0
- package/lib/notebook-generation.d.ts +8 -0
- package/lib/notebook-generation.js +12 -0
- package/lib/open-file-refresh-watcher-env.d.ts +4 -0
- package/lib/open-file-refresh-watcher-env.js +33 -0
- package/lib/open-file-refresh-watcher.d.ts +97 -0
- package/lib/open-file-refresh-watcher.js +190 -0
- package/lib/shell-utils.d.ts +6 -0
- package/lib/shell-utils.js +9 -0
- package/lib/task-target-notebook.d.ts +2 -0
- package/lib/task-target-notebook.js +28 -0
- package/lib/terminal-drag-format.d.ts +9 -0
- package/lib/terminal-drag-format.js +23 -0
- package/lib/terminal-drag.d.ts +12 -0
- package/lib/terminal-drag.js +268 -0
- package/lib/tokens.d.ts +149 -0
- package/lib/tokens.js +88 -0
- package/lib/tour/tour-anchors.d.ts +18 -0
- package/lib/tour/tour-anchors.js +18 -0
- package/lib/tour/tour-config.d.ts +66 -0
- package/lib/tour/tour-config.js +99 -0
- package/lib/tour/tour-defaults.json +58 -0
- package/lib/tour/tour-events.d.ts +19 -0
- package/lib/tour/tour-events.js +30 -0
- package/lib/tour/tour-overlay.d.ts +6 -0
- package/lib/tour/tour-overlay.js +350 -0
- package/lib/tour/tour-state.d.ts +20 -0
- package/lib/tour/tour-state.js +81 -0
- package/lib/tour/tour-steps.d.ts +33 -0
- package/lib/tour/tour-steps.js +216 -0
- package/lib/utils.d.ts +53 -0
- package/lib/utils.js +385 -0
- package/package.json +258 -0
- package/schema/plugin.json +42 -0
- package/src/api.ts +1424 -0
- package/src/cell-output-bundle.ts +176 -0
- package/src/cell-output-toolbar.ts +232 -0
- package/src/chat-progress-feedback.ts +35 -0
- package/src/chat-sidebar.tsx +5147 -0
- package/src/command-ids.ts +67 -0
- package/src/components/ask-user-question.tsx +151 -0
- package/src/components/checkbox.tsx +62 -0
- package/src/components/claude-mcp-panel.tsx +543 -0
- package/src/components/claude-mcp-paste.ts +132 -0
- package/src/components/claude-session-picker.tsx +214 -0
- package/src/components/form-dialog.tsx +75 -0
- package/src/components/launcher-picker.tsx +237 -0
- package/src/components/mcp-util.ts +53 -0
- package/src/components/notebook-generation-popover.tsx +127 -0
- package/src/components/pill.tsx +15 -0
- package/src/components/plugins-panel.tsx +774 -0
- package/src/components/settings-panel.tsx +1631 -0
- package/src/components/skills-panel.tsx +2084 -0
- package/src/handler.ts +51 -0
- package/src/icons.ts +71 -0
- package/src/index.ts +2583 -0
- package/src/markdown-renderer.tsx +153 -0
- package/src/notebook-generation-toolbar.tsx +281 -0
- package/src/notebook-generation.ts +23 -0
- package/src/open-file-refresh-watcher-env.ts +52 -0
- package/src/open-file-refresh-watcher.ts +260 -0
- package/src/shell-utils.ts +10 -0
- package/src/svg.d.ts +4 -0
- package/src/task-target-notebook.ts +37 -0
- package/src/terminal-drag-format.ts +29 -0
- package/src/terminal-drag.ts +382 -0
- package/src/tokens.ts +171 -0
- package/src/tour/tour-anchors.ts +21 -0
- package/src/tour/tour-config.ts +160 -0
- package/src/tour/tour-events.ts +34 -0
- package/src/tour/tour-overlay.tsx +474 -0
- package/src/tour/tour-state.ts +87 -0
- package/src/tour/tour-steps.ts +281 -0
- package/src/utils.ts +455 -0
- package/style/base.css +3238 -0
- package/style/icons/cell-toolbar-bug.svg +5 -0
- package/style/icons/cell-toolbar-chat.svg +5 -0
- package/style/icons/cell-toolbar-sparkle.svg +5 -0
- package/style/icons/claude.svg +1 -0
- package/style/icons/copilot-warning.svg +1 -0
- package/style/icons/copilot.svg +1 -0
- package/style/icons/copy.svg +1 -0
- package/style/icons/openai.svg +1 -0
- package/style/icons/opencode.svg +1 -0
- package/style/icons/sparkles-warning.svg +5 -0
- package/style/icons/sparkles.svg +1 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* localStorage-backed persistence for the first-run tour.
|
|
5
|
+
*
|
|
6
|
+
* The tour is shown exactly once per browser per major tour version. The
|
|
7
|
+
* version is bumped (in code) whenever the step set changes meaningfully
|
|
8
|
+
* so existing users get re-prompted instead of silently missing new
|
|
9
|
+
* onboarding content. Users can also re-run the tour on demand via the
|
|
10
|
+
* `notebook-intelligence:show-tour` command, which calls `resetTour()`.
|
|
11
|
+
*
|
|
12
|
+
* Why localStorage and not server-side state: the tour is purely UI; a
|
|
13
|
+
* round trip to the Jupyter server on every sidebar mount just to check
|
|
14
|
+
* a single flag would add latency to a hot path. localStorage is
|
|
15
|
+
* per-browser, which is the right granularity (a user signing in on a
|
|
16
|
+
* second browser benefits from seeing the tour again).
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// Bump when the tour's step set changes meaningfully enough to want
|
|
20
|
+
// existing users to re-see it. Keys older than the current version are
|
|
21
|
+
// ignored, and writes always use the current version.
|
|
22
|
+
export const TOUR_VERSION = 1;
|
|
23
|
+
// Exported so tests can clear the same key the production code writes,
|
|
24
|
+
// instead of duplicating the string literal.
|
|
25
|
+
export const TOUR_STORAGE_KEY = 'nbi.tour.completed';
|
|
26
|
+
const TOUR_KEY = TOUR_STORAGE_KEY;
|
|
27
|
+
|
|
28
|
+
interface ITourRecord {
|
|
29
|
+
version: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function safeStorage(): Storage | null {
|
|
33
|
+
// localStorage can throw under some sandboxed iframe / privacy modes;
|
|
34
|
+
// a thrown access here would crash the sidebar mount. Treat absence
|
|
35
|
+
// as "tour was never completed" rather than failing closed.
|
|
36
|
+
try {
|
|
37
|
+
return typeof window !== 'undefined' ? window.localStorage : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function hasCompletedTour(): boolean {
|
|
44
|
+
const storage = safeStorage();
|
|
45
|
+
if (!storage) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
const raw = storage.getItem(TOUR_KEY);
|
|
49
|
+
if (!raw) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(raw) as Partial<ITourRecord>;
|
|
54
|
+
return parsed?.version === TOUR_VERSION;
|
|
55
|
+
} catch {
|
|
56
|
+
// Stale / corrupt entry from an older NBI version. Treat as not
|
|
57
|
+
// completed; the next markCompleted will overwrite with the current
|
|
58
|
+
// shape.
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function markTourCompleted(): void {
|
|
64
|
+
const storage = safeStorage();
|
|
65
|
+
if (!storage) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
storage.setItem(TOUR_KEY, JSON.stringify({ version: TOUR_VERSION }));
|
|
70
|
+
} catch {
|
|
71
|
+
// Quota exceeded / private mode rejecting writes. Worst case the
|
|
72
|
+
// tour fires again on the next sidebar mount, which is preferable
|
|
73
|
+
// to crashing the sidebar.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function resetTour(): void {
|
|
78
|
+
const storage = safeStorage();
|
|
79
|
+
if (!storage) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
storage.removeItem(TOUR_KEY);
|
|
84
|
+
} catch {
|
|
85
|
+
// See markTourCompleted.
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Step definitions for the first-run tour.
|
|
5
|
+
*
|
|
6
|
+
* The copy (titles, descriptions, button labels, command palette
|
|
7
|
+
* label) lives in `tour-defaults.json`, which uses the same schema as
|
|
8
|
+
* an admin override file (see `docs/admin-tour-config.md`). The
|
|
9
|
+
* structural wiring (DOM anchor ids, placements, capability gates)
|
|
10
|
+
* lives in TypeScript because it isn't user-facing copy.
|
|
11
|
+
*
|
|
12
|
+
* Each step anchors to a DOM element by `data-tour-id` (added on the
|
|
13
|
+
* matching React element). Using a dedicated attribute rather than a
|
|
14
|
+
* CSS class makes the contract explicit and decouples the tour from
|
|
15
|
+
* styling changes.
|
|
16
|
+
*
|
|
17
|
+
* The `requires` predicate filters out steps that don't apply to the
|
|
18
|
+
* current deployment (e.g. the Claude history step is skipped when
|
|
19
|
+
* Claude CLI is not installed). Predicates close over the snapshot of
|
|
20
|
+
* capabilities at tour start, so a CLI being installed mid-tour does
|
|
21
|
+
* not retroactively insert a step.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { NBIAPI } from '../api';
|
|
25
|
+
import {
|
|
26
|
+
applyTourOverrides,
|
|
27
|
+
ITourOverrides,
|
|
28
|
+
launcherTileTemplates
|
|
29
|
+
} from './tour-config';
|
|
30
|
+
import { TOUR_ANCHOR } from './tour-anchors';
|
|
31
|
+
import defaultsJson from './tour-defaults.json';
|
|
32
|
+
|
|
33
|
+
export type TourPlacement = 'top' | 'bottom' | 'left' | 'right' | 'center';
|
|
34
|
+
|
|
35
|
+
export interface ITourStep {
|
|
36
|
+
id: string;
|
|
37
|
+
title: string;
|
|
38
|
+
// Description can be a string OR a thunk that's evaluated when the
|
|
39
|
+
// step list is built. The thunk form lets a step adapt its copy to
|
|
40
|
+
// the deployment (e.g. the launcher-tiles step lists only the CLIs
|
|
41
|
+
// that are actually installed on this machine).
|
|
42
|
+
description: string | (() => string);
|
|
43
|
+
// Element selector: tour anchors to `[data-tour-id="<anchorId>"]`.
|
|
44
|
+
// null means a centered modal step (welcome / completion).
|
|
45
|
+
anchorId: string | null;
|
|
46
|
+
placement: TourPlacement;
|
|
47
|
+
// Optional predicate. The tour fires this once when computing the
|
|
48
|
+
// step list; a return of false skips the step entirely. Keep
|
|
49
|
+
// predicates pure and cheap.
|
|
50
|
+
requires?: () => boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface IDefaultsSchema {
|
|
54
|
+
command?: { label?: string };
|
|
55
|
+
ui?: { skip?: string; next?: string; back?: string; done?: string };
|
|
56
|
+
steps?: Record<
|
|
57
|
+
string,
|
|
58
|
+
{
|
|
59
|
+
title?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
description_singular?: string;
|
|
62
|
+
description_plural?: string;
|
|
63
|
+
}
|
|
64
|
+
>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Bundled defaults file. Same schema as an admin override; the
|
|
68
|
+
// override applier overlays the admin layer on top of these.
|
|
69
|
+
export const TOUR_DEFAULTS: IDefaultsSchema = defaultsJson as IDefaultsSchema;
|
|
70
|
+
|
|
71
|
+
function defaultText(stepId: string, key: 'title' | 'description'): string {
|
|
72
|
+
const step = TOUR_DEFAULTS.steps?.[stepId];
|
|
73
|
+
const value = step?.[key];
|
|
74
|
+
return typeof value === 'string' ? value : '';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Map every Coding Agent launcher id to (a) the capability flag that
|
|
78
|
+
// decides whether the tile shows up at all and (b) the display name
|
|
79
|
+
// the tour should use. Order matches what users see in the JL
|
|
80
|
+
// Launcher.
|
|
81
|
+
const CODING_AGENT_LAUNCHERS: ReadonlyArray<{
|
|
82
|
+
id: string;
|
|
83
|
+
label: string;
|
|
84
|
+
available: () => boolean;
|
|
85
|
+
}> = [
|
|
86
|
+
{
|
|
87
|
+
id: 'claude-code',
|
|
88
|
+
label: 'Claude Code',
|
|
89
|
+
available: () => NBIAPI.config.isClaudeCliAvailable
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'codex',
|
|
93
|
+
label: 'Codex',
|
|
94
|
+
available: () => NBIAPI.config.isCodexCliAvailable
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'opencode',
|
|
98
|
+
label: 'opencode',
|
|
99
|
+
available: () => NBIAPI.config.isOpenCodeCliAvailable
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'pi',
|
|
103
|
+
label: 'Pi',
|
|
104
|
+
available: () => NBIAPI.config.isPiCliAvailable
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'github-copilot-cli',
|
|
108
|
+
label: 'GitHub Copilot CLI',
|
|
109
|
+
available: () => NBIAPI.config.isGitHubCopilotCliAvailable
|
|
110
|
+
}
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
function visibleCodingAgentLaunchers(): string[] {
|
|
114
|
+
return CODING_AGENT_LAUNCHERS.filter(
|
|
115
|
+
l =>
|
|
116
|
+
l.available() &&
|
|
117
|
+
!NBIAPI.config.isCodingAgentLauncherDisabledByPolicy(l.id)
|
|
118
|
+
).map(l => l.label);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function formatLauncherList(names: string[]): string {
|
|
122
|
+
if (names.length === 0) {
|
|
123
|
+
return '';
|
|
124
|
+
}
|
|
125
|
+
if (names.length === 1) {
|
|
126
|
+
return names[0];
|
|
127
|
+
}
|
|
128
|
+
if (names.length === 2) {
|
|
129
|
+
return `${names[0]} and ${names[1]}`;
|
|
130
|
+
}
|
|
131
|
+
return `${names.slice(0, -1).join(', ')}, and ${names[names.length - 1]}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function launcherTilesDescription(): string {
|
|
135
|
+
const names = visibleCodingAgentLaunchers();
|
|
136
|
+
const list = formatLauncherList(names);
|
|
137
|
+
// Admin overrides win; defaults supply the fallback templates.
|
|
138
|
+
const overrideTemplates = launcherTileTemplates(
|
|
139
|
+
NBIAPI.config.tourOverrides as ITourOverrides
|
|
140
|
+
);
|
|
141
|
+
const defaultStep = TOUR_DEFAULTS.steps?.['launcher-tiles'];
|
|
142
|
+
const template =
|
|
143
|
+
names.length === 1
|
|
144
|
+
? (overrideTemplates.singular ?? defaultStep?.description_singular)
|
|
145
|
+
: (overrideTemplates.plural ?? defaultStep?.description_plural);
|
|
146
|
+
return template ? template.replace(/\{launchers\}/g, list) : '';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const ALL_TOUR_STEPS: readonly ITourStep[] = Object.freeze([
|
|
150
|
+
{
|
|
151
|
+
id: 'welcome',
|
|
152
|
+
title: defaultText('welcome', 'title'),
|
|
153
|
+
description: defaultText('welcome', 'description'),
|
|
154
|
+
anchorId: null,
|
|
155
|
+
placement: 'center'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'new-chat',
|
|
159
|
+
title: defaultText('new-chat', 'title'),
|
|
160
|
+
description: defaultText('new-chat', 'description'),
|
|
161
|
+
anchorId: TOUR_ANCHOR.newChat,
|
|
162
|
+
placement: 'bottom',
|
|
163
|
+
// The + button is only rendered in Claude mode (it restarts the
|
|
164
|
+
// Claude client). Skip when not in Claude mode so the tour doesn't
|
|
165
|
+
// describe a missing affordance.
|
|
166
|
+
requires: () => NBIAPI.config.isInClaudeCodeMode
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'claude-history',
|
|
170
|
+
title: defaultText('claude-history', 'title'),
|
|
171
|
+
description: defaultText('claude-history', 'description'),
|
|
172
|
+
anchorId: TOUR_ANCHOR.claudeHistory,
|
|
173
|
+
placement: 'bottom',
|
|
174
|
+
// Visible only when Claude CLI is available AND Claude mode is on.
|
|
175
|
+
// Without the second guard the anchor isn't even rendered, so the
|
|
176
|
+
// tour would silently skip; the `requires` keeps the skip
|
|
177
|
+
// explicit.
|
|
178
|
+
requires: () =>
|
|
179
|
+
NBIAPI.config.isClaudeCliAvailable && NBIAPI.config.isInClaudeCodeMode
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: 'settings-gear',
|
|
183
|
+
title: defaultText('settings-gear', 'title'),
|
|
184
|
+
description: defaultText('settings-gear', 'description'),
|
|
185
|
+
anchorId: TOUR_ANCHOR.settingsGear,
|
|
186
|
+
placement: 'bottom'
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'slash-commands',
|
|
190
|
+
title: defaultText('slash-commands', 'title'),
|
|
191
|
+
description: defaultText('slash-commands', 'description'),
|
|
192
|
+
anchorId: TOUR_ANCHOR.slashCommands,
|
|
193
|
+
placement: 'top'
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'add-context',
|
|
197
|
+
title: defaultText('add-context', 'title'),
|
|
198
|
+
description: defaultText('add-context', 'description'),
|
|
199
|
+
anchorId: TOUR_ANCHOR.addContext,
|
|
200
|
+
placement: 'top'
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'upload-file',
|
|
204
|
+
title: defaultText('upload-file', 'title'),
|
|
205
|
+
description: defaultText('upload-file', 'description'),
|
|
206
|
+
anchorId: TOUR_ANCHOR.uploadFile,
|
|
207
|
+
placement: 'top'
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
id: 'drag-and-drop',
|
|
211
|
+
title: defaultText('drag-and-drop', 'title'),
|
|
212
|
+
description: defaultText('drag-and-drop', 'description'),
|
|
213
|
+
anchorId: TOUR_ANCHOR.promptInput,
|
|
214
|
+
placement: 'top'
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'chat-mode',
|
|
218
|
+
title: defaultText('chat-mode', 'title'),
|
|
219
|
+
description: defaultText('chat-mode', 'description'),
|
|
220
|
+
anchorId: TOUR_ANCHOR.chatMode,
|
|
221
|
+
placement: 'top',
|
|
222
|
+
// Mode picker is hidden in Claude mode (Claude owns its own loop).
|
|
223
|
+
requires: () => !NBIAPI.config.isInClaudeCodeMode
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
id: 'launcher-tiles',
|
|
227
|
+
title: defaultText('launcher-tiles', 'title'),
|
|
228
|
+
// Dynamic description so the step only mentions the CLI tools the
|
|
229
|
+
// user actually has installed. Skipped entirely when zero are
|
|
230
|
+
// available (see the requires predicate below). Both the default
|
|
231
|
+
// templates and admin overrides flow through this thunk so the
|
|
232
|
+
// {launchers} placeholder gets substituted with the same
|
|
233
|
+
// comma-joined list either way.
|
|
234
|
+
description: launcherTilesDescription,
|
|
235
|
+
anchorId: null,
|
|
236
|
+
placement: 'center',
|
|
237
|
+
// Skip the step on machines where no coding-agent CLI is installed
|
|
238
|
+
// (or admin policy hides every tile) so the tour doesn't promise an
|
|
239
|
+
// affordance the user can't reach.
|
|
240
|
+
requires: () => visibleCodingAgentLaunchers().length > 0
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
id: 'done',
|
|
244
|
+
title: defaultText('done', 'title'),
|
|
245
|
+
description: defaultText('done', 'description'),
|
|
246
|
+
anchorId: null,
|
|
247
|
+
placement: 'center'
|
|
248
|
+
}
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
// What the overlay actually consumes: every thunk has already been
|
|
252
|
+
// resolved to a plain string. Exposing this separately keeps the
|
|
253
|
+
// overlay's render code type-safe without the thunk variant.
|
|
254
|
+
export type IResolvedTourStep = Omit<ITourStep, 'description'> & {
|
|
255
|
+
description: string;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
export function activeTourSteps(): IResolvedTourStep[] {
|
|
259
|
+
// Pipeline:
|
|
260
|
+
// 1. Apply admin overrides (title/description rewrites, per-step
|
|
261
|
+
// enabled:false drops). The default-overrides path returns a
|
|
262
|
+
// shallow copy so the rest of the pipeline doesn't mutate the
|
|
263
|
+
// ALL_TOUR_STEPS module-level constant.
|
|
264
|
+
// 2. Filter on the per-step `requires()` predicate (capability /
|
|
265
|
+
// mode gates).
|
|
266
|
+
// 3. Resolve any thunk descriptions to plain strings so the
|
|
267
|
+
// overlay can render them directly. Doing this here (rather
|
|
268
|
+
// than at render time) matches the "snapshot on mount"
|
|
269
|
+
// semantics: a capability change mid-tour doesn't reshape step
|
|
270
|
+
// copy under the user.
|
|
271
|
+
const overrides = NBIAPI.config.tourOverrides as ITourOverrides;
|
|
272
|
+
return applyTourOverrides(ALL_TOUR_STEPS, overrides)
|
|
273
|
+
.filter(step => (step.requires ? step.requires() : true))
|
|
274
|
+
.map(step => ({
|
|
275
|
+
...step,
|
|
276
|
+
description:
|
|
277
|
+
typeof step.description === 'function'
|
|
278
|
+
? step.description()
|
|
279
|
+
: step.description
|
|
280
|
+
}));
|
|
281
|
+
}
|