@cueloop/client 0.1.0-alpha.9 → 0.1.0-alpha.90
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/native/build-pty.sh +51 -0
- package/native/build.sh +82 -0
- package/native/darwin-arm64/libcuelooppty.dylib +0 -0
- package/native/darwin-arm64/libcueloopvt.dylib +0 -0
- package/native/src/pty.zig +193 -0
- package/native/src/shim.zig +114 -0
- package/package.json +42 -34
- package/src/App.appearance.test.tsx +53 -0
- package/src/App.autoclose.test.tsx +77 -51
- package/src/App.diff.test.tsx +259 -45
- package/src/App.export.test.tsx +58 -41
- package/src/App.focus.test.ts +89 -0
- package/src/App.identity.test.tsx +78 -0
- package/src/App.plan-render.test.tsx +209 -0
- package/src/App.plan-review.test.tsx +330 -161
- package/src/App.prototype-markdown.test.tsx +128 -0
- package/src/App.prototype.test.tsx +176 -0
- package/src/App.readonly.test.tsx +129 -67
- package/src/App.regression.test.tsx +94 -0
- package/src/App.rendering.test.tsx +61 -35
- package/src/App.share-dialog.test.tsx +172 -0
- package/src/App.submit-confirm.test.tsx +264 -0
- package/src/App.test.tsx +402 -126
- package/src/App.toast.test.tsx +98 -0
- package/src/App.tsx +1402 -1060
- package/src/App.visual.test.tsx +57 -0
- package/src/App.walk.test.tsx +299 -0
- package/src/App.workbench.test.tsx +774 -0
- package/src/__snapshots__/view-plan.regression.test.ts.snap +100 -0
- package/src/annotation-palette.ts +62 -0
- package/src/ansi-screen.test.ts +28 -0
- package/src/ansi-screen.ts +72 -0
- package/src/app-key-state.ts +61 -0
- package/src/app-screens.tsx +577 -0
- package/src/app-view-model.ts +168 -0
- package/src/attribution.test.ts +31 -0
- package/src/attribution.ts +42 -0
- package/src/client-extension-registry.test.tsx +61 -0
- package/src/client-extension-registry.ts +159 -0
- package/src/clipboard.test.ts +23 -0
- package/src/clipboard.ts +33 -0
- package/src/components/AnnotatableFileView.stories.tsx +42 -0
- package/src/components/AnnotatableFileView.test.tsx +102 -0
- package/src/components/AnnotatableFileView.tsx +134 -0
- package/src/components/AnnotationCards.stories.tsx +86 -0
- package/src/components/AnnotationCards.tsx +341 -0
- package/src/components/AnsiScreen.stories.tsx +12 -0
- package/src/components/AnsiScreen.tsx +28 -0
- package/src/components/AppHeader.stories.tsx +50 -0
- package/src/components/AppHeader.tsx +83 -0
- package/src/components/AppShell.brand.test.tsx +58 -0
- package/src/components/AppShell.extensions.test.tsx +191 -0
- package/src/components/AppShell.stories.tsx +89 -0
- package/src/components/AppShell.tsx +362 -0
- package/src/components/AppShell.zoom.test.tsx +135 -0
- package/src/components/BareWorkbenchFileView.stories.tsx +27 -0
- package/src/components/BareWorkbenchFileView.tsx +64 -0
- package/src/components/Breadcrumb.stories.tsx +19 -0
- package/src/components/Breadcrumb.tsx +45 -0
- package/src/components/ChangesColumn.stories.tsx +44 -0
- package/src/components/ChangesColumn.tsx +199 -0
- package/src/components/ChangesFileTree.test.tsx +61 -0
- package/src/components/CodeBlock.stories.tsx +42 -0
- package/src/components/CodeBlock.tsx +84 -0
- package/src/components/CompletionOverlay.stories.tsx +37 -0
- package/src/components/CompletionOverlay.tsx +77 -0
- package/src/components/ConfirmCard.stories.tsx +37 -0
- package/src/components/ConfirmCard.tsx +150 -0
- package/src/components/ConfirmDialog.stories.tsx +21 -0
- package/src/components/ConfirmDialog.tsx +72 -0
- package/src/components/DiffContentView.scroll.test.tsx +149 -0
- package/src/components/DiffContentView.split.test.tsx +140 -0
- package/src/components/DiffContentView.stories.tsx +80 -0
- package/src/components/DiffContentView.syntax.test.tsx +82 -0
- package/src/components/DiffContentView.tsx +906 -0
- package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
- package/src/components/DiscussionMarkerRail.tsx +186 -0
- package/src/components/EditorGrid.stories.tsx +59 -0
- package/src/components/EditorGrid.tabs.test.tsx +118 -0
- package/src/components/EditorGrid.tsx +426 -0
- package/src/components/EditorGrid.zoom.test.tsx +68 -0
- package/src/components/ExtensionZone.stories.tsx +28 -0
- package/src/components/ExtensionZone.tsx +145 -0
- package/src/components/FileContentsView.stories.tsx +16 -0
- package/src/components/FileContentsView.test.tsx +29 -0
- package/src/components/FileContentsView.tsx +98 -0
- package/src/components/GridTabContent.stories.tsx +59 -0
- package/src/components/GridTabContent.tsx +182 -0
- package/src/components/HandleChipsInput.stories.tsx +24 -0
- package/src/components/HandleChipsInput.test.tsx +35 -0
- package/src/components/HandleChipsInput.tsx +145 -0
- package/src/components/KeybindsDialog.stories.tsx +31 -0
- package/src/components/KeybindsDialog.tsx +103 -0
- package/src/components/MarkdownGridBlock.stories.tsx +33 -0
- package/src/components/MarkdownGridBlock.tsx +72 -0
- package/src/components/MarkdownThreadEditor.stories.tsx +15 -0
- package/src/components/MarkdownThreadEditor.test.tsx +98 -0
- package/src/components/MarkdownThreadEditor.tsx +162 -0
- package/src/components/MenuBar.stories.tsx +28 -0
- package/src/components/MenuBar.tsx +44 -0
- package/src/components/NavModeHint.stories.tsx +40 -0
- package/src/components/NavModeHint.tsx +31 -0
- package/src/components/OverlayScrollbar.stories.tsx +33 -0
- package/src/components/OverlayScrollbar.test.tsx +139 -0
- package/src/components/OverlayScrollbar.tsx +129 -0
- package/src/components/PaneGrid.stories.tsx +84 -0
- package/src/components/PaneGrid.tsx +128 -0
- package/src/components/PanelColumn.stories.tsx +25 -0
- package/src/components/PanelColumn.tsx +98 -0
- package/src/components/ProjectTreeView.stories.tsx +25 -0
- package/src/components/ProjectTreeView.test.tsx +75 -0
- package/src/components/ProjectTreeView.tsx +125 -0
- package/src/components/PromptDialog.stories.tsx +36 -0
- package/src/components/PromptDialog.test.tsx +61 -0
- package/src/components/PromptDialog.tsx +99 -0
- package/src/components/PrototypeContentView.stories.tsx +19 -0
- package/src/components/PrototypeContentView.tsx +446 -0
- package/src/components/RootOverlay.stories.tsx +45 -0
- package/src/components/RootOverlay.tsx +48 -0
- package/src/components/ScrollArea.stories.tsx +20 -0
- package/src/components/ScrollArea.tsx +44 -0
- package/src/components/SettingsDialog.stories.tsx +117 -0
- package/src/components/SettingsDialog.tsx +186 -0
- package/src/components/SettingsRows.stories.tsx +18 -0
- package/src/components/SettingsRows.tsx +112 -0
- package/src/components/ShareDialog.stories.tsx +121 -0
- package/src/components/ShareDialog.tsx +552 -0
- package/src/components/ShellHeader.stories.tsx +50 -0
- package/src/components/ShellHeader.tsx +137 -0
- package/src/components/SlashComposer.stories.tsx +37 -0
- package/src/components/SlashComposer.tsx +87 -0
- package/src/components/ThreadFooter.stories.tsx +30 -0
- package/src/components/ThreadFooter.test.tsx +47 -0
- package/src/components/ThreadFooter.tsx +76 -0
- package/src/components/ThreadTree.stories.tsx +46 -0
- package/src/components/ThreadTree.tsx +255 -0
- package/src/components/ThreadView.selection.test.tsx +951 -0
- package/src/components/ThreadView.slash.test.tsx +146 -0
- package/src/components/ThreadView.stories.tsx +26 -0
- package/src/components/ThreadView.tsx +525 -0
- package/src/components/Toast.stories.tsx +19 -0
- package/src/components/Toast.tsx +52 -0
- package/src/components/Tooltip.stories.tsx +26 -0
- package/src/components/Tooltip.tsx +72 -0
- package/src/components/TreePane.stories.tsx +106 -0
- package/src/components/TreePane.tsx +157 -0
- package/src/components/WalkWizard.stories.tsx +100 -0
- package/src/components/WalkWizard.tsx +150 -0
- package/src/components/WelcomePlayground.stories.tsx +12 -0
- package/src/components/WelcomePlayground.test.tsx +49 -0
- package/src/components/WelcomePlayground.tsx +139 -0
- package/src/components/WorkspacePanels.stories.tsx +36 -0
- package/src/components/WorkspacePanels.tsx +116 -0
- package/src/components/__snapshots__/stories.test.tsx.snap +2264 -0
- package/src/components/diff-content-view-layout.test.ts +198 -0
- package/src/components/diff-content-view-layout.ts +84 -0
- package/src/components/editor-grid.test.ts +86 -0
- package/src/components/editor-grid.ts +138 -0
- package/src/components/file-tree.test.ts +60 -0
- package/src/components/file-tree.ts +112 -0
- package/src/components/menu-control.stories.tsx +35 -0
- package/src/components/menu-control.tsx +43 -0
- package/src/components/pages/Workspace.stories.tsx +181 -0
- package/src/components/primitives/Button.stories.tsx +32 -0
- package/src/components/primitives/Button.tsx +59 -0
- package/src/components/primitives/Card.stories.tsx +25 -0
- package/src/components/primitives/Card.tsx +63 -0
- package/src/components/primitives/Dialog.stories.tsx +24 -0
- package/src/components/primitives/Dialog.test.tsx +37 -0
- package/src/components/primitives/Dialog.tsx +71 -0
- package/src/components/primitives/DialogActions.stories.tsx +20 -0
- package/src/components/primitives/DialogActions.tsx +39 -0
- package/src/components/primitives/IconButton.stories.tsx +36 -0
- package/src/components/primitives/IconButton.tsx +62 -0
- package/src/components/primitives/StatusBar.stories.tsx +11 -0
- package/src/components/primitives/StatusBar.tsx +23 -0
- package/src/components/primitives/Switch.stories.tsx +17 -0
- package/src/components/primitives/Switch.tsx +32 -0
- package/src/components/primitives/Tabs.stories.tsx +29 -0
- package/src/components/primitives/Tabs.tsx +102 -0
- package/src/components/primitives/Toolbar.stories.tsx +17 -0
- package/src/components/primitives/Toolbar.tsx +14 -0
- package/src/components/primitives/Tree.stories.tsx +124 -0
- package/src/components/primitives/Tree.tsx +100 -0
- package/src/components/primitives/frame.test.ts +12 -0
- package/src/components/primitives/frame.ts +17 -0
- package/src/components/primitives/icons.ts +61 -0
- package/src/components/primitives/tree-model.test.ts +99 -0
- package/src/components/primitives/tree-model.ts +137 -0
- package/src/components/quick-actions-editor.stories.tsx +58 -0
- package/src/components/quick-actions-editor.test.tsx +117 -0
- package/src/components/quick-actions-editor.tsx +226 -0
- package/src/components/share-dialog-store.ts +58 -0
- package/src/components/stories-app.tsx +193 -0
- package/src/components/stories.test.tsx +74 -0
- package/src/components/story-fixtures.ts +102 -0
- package/src/components/story-tree.test.ts +108 -0
- package/src/components/story.ts +159 -0
- package/src/components/syntax-highlight.ts +133 -0
- package/src/components/terminal-pane.test.tsx +40 -0
- package/src/components/terminal-pane.ts +199 -0
- package/src/components/theme-context.stories.tsx +46 -0
- package/src/components/theme-context.tsx +37 -0
- package/src/components/thread-tree.test.ts +134 -0
- package/src/components/thread-tree.ts +109 -0
- package/src/components/truncate-text.ts +7 -0
- package/src/components/truncate-title.test.ts +31 -0
- package/src/components/truncate-title.ts +15 -0
- package/src/config-identity.test.ts +78 -0
- package/src/config.test.ts +579 -34
- package/src/config.ts +533 -57
- package/src/diff-fold.test.ts +78 -0
- package/src/diff-fold.ts +126 -0
- package/src/diff-hunk-curate.test.ts +315 -0
- package/src/diff-hunk-curate.ts +153 -0
- package/src/diff-intraline.test.ts +238 -0
- package/src/diff-intraline.ts +236 -0
- package/src/diff-syntax.test.ts +112 -0
- package/src/diff-syntax.ts +179 -0
- package/src/discussions.ts +79 -0
- package/src/editor.test.ts +149 -0
- package/src/editor.ts +139 -9
- package/src/ghostty-terminal.test.ts +85 -0
- package/src/ghostty-terminal.ts +199 -0
- package/src/github-identity.test.ts +58 -0
- package/src/github-identity.ts +41 -0
- package/src/herdr.test.ts +9 -34
- package/src/herdr.ts +25 -21
- package/src/index.ts +28 -0
- package/src/integrations.test.ts +41 -0
- package/src/integrations.ts +32 -0
- package/src/intent-dispatch.test.ts +724 -0
- package/src/intent-dispatch.ts +690 -0
- package/src/key-bindings.test.ts +156 -0
- package/src/key-bindings.ts +459 -0
- package/src/keymap.test.ts +456 -115
- package/src/keymap.ts +315 -81
- package/src/kitty-image.test.ts +66 -0
- package/src/kitty-image.ts +138 -0
- package/src/launch-layout.test.ts +67 -0
- package/src/launch-layout.ts +47 -0
- package/src/mark-runs.ts +121 -0
- package/src/markdown-editor-syntax-style.ts +50 -0
- package/src/markdown-grid.test.ts +101 -0
- package/src/markdown-grid.ts +203 -0
- package/src/markdown-highlight.test.ts +61 -0
- package/src/markdown-highlight.ts +122 -0
- package/src/pasted-image.test.ts +67 -0
- package/src/pasted-image.ts +60 -0
- package/src/perf/otlp-trace.test.ts +116 -0
- package/src/perf/otlp-trace.ts +130 -0
- package/src/perf/perf-report.ts +17 -0
- package/src/perf/perf-timings.test.ts +18 -0
- package/src/perf/perf-timings.ts +38 -0
- package/src/prototype-browser.test.ts +81 -0
- package/src/prototype-browser.ts +329 -0
- package/src/prototype-pixels.tsx +35 -0
- package/src/pty.test.ts +81 -0
- package/src/pty.ts +245 -0
- package/src/ready-signal.test.ts +85 -0
- package/src/ready-signal.ts +52 -0
- package/src/run.ts +85 -12
- package/src/serve.test.ts +24 -12
- package/src/serve.ts +52 -10
- package/src/share.ts +237 -0
- package/src/skills.test.ts +74 -0
- package/src/skills.ts +75 -0
- package/src/slash-palette.test.ts +103 -0
- package/src/slash-palette.ts +129 -0
- package/src/split-diff.test.ts +110 -0
- package/src/split-diff.ts +149 -0
- package/src/test-support.test.ts +264 -0
- package/src/test-support.ts +369 -0
- package/src/theme-presets.test.ts +51 -0
- package/src/theme-presets.ts +219 -0
- package/src/theme.ts +88 -19
- package/src/thread-chords.test.ts +79 -0
- package/src/thread-chords.ts +163 -0
- package/src/thread-code-syntax.ts +45 -0
- package/src/thread-controller.curate.test.ts +476 -0
- package/src/thread-controller.derivation.test.ts +62 -0
- package/src/thread-controller.live-diff.test.ts +322 -0
- package/src/thread-controller.optimistic.test.ts +69 -0
- package/src/thread-controller.share.test.ts +561 -0
- package/src/thread-controller.switch-cache.test.ts +176 -0
- package/src/thread-controller.tree.test.ts +261 -0
- package/src/thread-controller.ts +2050 -0
- package/src/thread-selection.test.ts +210 -0
- package/src/thread-selection.ts +243 -0
- package/src/tree-view.test.ts +128 -0
- package/src/tree-view.ts +153 -0
- package/src/use-annotation-surface.tsx +975 -0
- package/src/use-changes-workbench.ts +190 -0
- package/src/use-frame-measure.ts +41 -0
- package/src/use-remember-layout.ts +31 -0
- package/src/use-repo-changes.ts +87 -0
- package/src/use-settings-dialog.tsx +364 -0
- package/src/use-terminal-virtualizer.ts +259 -0
- package/src/use-thread-body-editing.tsx +58 -0
- package/src/version.ts +5 -0
- package/src/view-diff.test.ts +279 -9
- package/src/view-diff.ts +460 -24
- package/src/view-plan.regression.test.ts +60 -0
- package/src/view-plan.test.ts +545 -0
- package/src/view-plan.ts +1019 -0
- package/src/visual/contrast.test.ts +35 -0
- package/src/visual/contrast.ts +33 -0
- package/src/walk.test.ts +121 -0
- package/src/walk.ts +74 -0
- package/src/workbench-snapshot.test.ts +99 -0
- package/src/workbench-snapshot.ts +59 -0
- package/src/session-controller.ts +0 -398
- package/src/syntax.test.ts +0 -41
- package/src/syntax.ts +0 -88
- package/src/view.test.ts +0 -157
- package/src/view.ts +0 -364
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The welcome page is a real annotation surface, but ephemeral: selecting its copy and typing leaves
|
|
3
|
+
* a comment that shows inline, exactly as a file does, and it lives only in the component - there is
|
|
4
|
+
* no controller or daemon behind it, so nothing is persisted. The copy prompts the two first gestures.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, test } from "bun:test";
|
|
8
|
+
import { testRender } from "@opentui/react/test-utils";
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { WelcomePlayground } from "./WelcomePlayground";
|
|
11
|
+
import { DARK } from "../theme";
|
|
12
|
+
import { CLIENT_VERSION } from "../version";
|
|
13
|
+
import { dragText, typeText, pressKey, waitForText } from "../test-support";
|
|
14
|
+
|
|
15
|
+
describe("WelcomePlayground", () => {
|
|
16
|
+
test("the copy prompts the select-and-type and the slash gestures", async () => {
|
|
17
|
+
const setup = await testRender(<WelcomePlayground quickActions={[]} theme={DARK} />, {
|
|
18
|
+
width: 80,
|
|
19
|
+
height: 24,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await waitForText(setup, "Getting started");
|
|
23
|
+
const frame = setup.captureCharFrame();
|
|
24
|
+
|
|
25
|
+
expect(frame).toContain("start typing to leave your first comment");
|
|
26
|
+
expect(frame).toContain('type "/"');
|
|
27
|
+
expect(frame).toContain(`cueloop v${CLIENT_VERSION}`);
|
|
28
|
+
|
|
29
|
+
setup.renderer.destroy();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("selecting the copy and typing leaves a comment inline, exactly like a file", async () => {
|
|
33
|
+
const setup = await testRender(<WelcomePlayground quickActions={[]} theme={DARK} />, {
|
|
34
|
+
width: 80,
|
|
35
|
+
height: 24,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Act - select the practice line, type a comment, send it
|
|
39
|
+
await waitForText(setup, "quick brown fox");
|
|
40
|
+
await dragText(setup, "quick brown fox", "quick brown fox", "quick brown fox".length);
|
|
41
|
+
await typeText(setup, "my first note");
|
|
42
|
+
await pressKey(setup, "RETURN", { meta: true });
|
|
43
|
+
|
|
44
|
+
// Assert - the note renders on the surface, no controller or daemon involved
|
|
45
|
+
await waitForText(setup, "my first note");
|
|
46
|
+
|
|
47
|
+
setup.renderer.destroy();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// The welcome page as a live annotation playground: the onboarding copy renders through the same
|
|
2
|
+
// diff sheet a file uses, so selecting text and typing leaves a comment with the identical gesture a
|
|
3
|
+
// real review uses. The notes are ephemeral - there is no file or thread behind onboarding copy, so
|
|
4
|
+
// they live only in this component's state and are never stored or re-anchored. The copy teaches the
|
|
5
|
+
// two first gestures: select-and-type to comment, and "/" for the quick actions and skills.
|
|
6
|
+
|
|
7
|
+
import React, { useMemo, useState } from "react";
|
|
8
|
+
import {
|
|
9
|
+
makeAnchor,
|
|
10
|
+
newAnnotationId,
|
|
11
|
+
SCHEMA_VERSION,
|
|
12
|
+
type Annotation,
|
|
13
|
+
type Thread,
|
|
14
|
+
} from "@cueloop/schema";
|
|
15
|
+
import type { Theme } from "../theme";
|
|
16
|
+
import type { QuickAction } from "../config";
|
|
17
|
+
import { CLIENT_VERSION } from "../version";
|
|
18
|
+
import { DiffContentView } from "./DiffContentView";
|
|
19
|
+
import { diffRowBlocks, fileContentsRows, marksByRows } from "../view-diff";
|
|
20
|
+
|
|
21
|
+
export interface WelcomePlaygroundProps {
|
|
22
|
+
quickActions: QuickAction[];
|
|
23
|
+
/** Reports whether the playground composer is open, so the shell suspends its inbox keys while typing. */
|
|
24
|
+
onComposingChange?: (composing: boolean) => void;
|
|
25
|
+
suspended?: boolean;
|
|
26
|
+
theme?: Theme;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const DOCS_URL = "https://cueloop.dev/docs/";
|
|
30
|
+
|
|
31
|
+
/** The playground copy: a heading, the two guided prompts, a line to practice on, and the links. */
|
|
32
|
+
function welcomeCopy(version: string): string {
|
|
33
|
+
return [
|
|
34
|
+
"Getting started",
|
|
35
|
+
"Comment on agent-authored work.",
|
|
36
|
+
"",
|
|
37
|
+
"This page is a playground. Nothing you write here is saved.",
|
|
38
|
+
"",
|
|
39
|
+
"Select the line below and start typing to leave your first comment:",
|
|
40
|
+
"",
|
|
41
|
+
" The quick brown fox jumps over the lazy dog.",
|
|
42
|
+
"",
|
|
43
|
+
'While composing, type "/" to see the quick actions and skills.',
|
|
44
|
+
"",
|
|
45
|
+
"Start",
|
|
46
|
+
" - select a thread",
|
|
47
|
+
" - inspect files and changes",
|
|
48
|
+
"",
|
|
49
|
+
"Learn",
|
|
50
|
+
` - docs ${DOCS_URL}`,
|
|
51
|
+
"",
|
|
52
|
+
"Version",
|
|
53
|
+
` - cueloop v${version}`,
|
|
54
|
+
].join("\n");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** An ephemeral thread that holds nothing but the playground's notes, so the surface groups
|
|
58
|
+
* discussions exactly as a real review does without any of it reaching the daemon. */
|
|
59
|
+
function playgroundThread(annotations: Annotation[]): Thread {
|
|
60
|
+
return {
|
|
61
|
+
schemaVersion: SCHEMA_VERSION,
|
|
62
|
+
id: "welcome",
|
|
63
|
+
workspace: { repoRoot: "", branch: "main" },
|
|
64
|
+
artifact: { type: "plan", content: "", meta: {} },
|
|
65
|
+
revisions: [],
|
|
66
|
+
annotations,
|
|
67
|
+
message: null,
|
|
68
|
+
status: "pending",
|
|
69
|
+
createdAt: "1970-01-01T00:00:00.000Z",
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function WelcomePlayground(props: WelcomePlaygroundProps): React.ReactNode {
|
|
74
|
+
const [annotations, setAnnotations] = useState<Annotation[]>([]);
|
|
75
|
+
const rows = useMemo(() => fileContentsRows("welcome", welcomeCopy(CLIENT_VERSION)), []);
|
|
76
|
+
const session = playgroundThread(annotations);
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<DiffContentView
|
|
80
|
+
rows={rows}
|
|
81
|
+
session={session}
|
|
82
|
+
marks={marksByRows(annotations, rows)}
|
|
83
|
+
quickActions={props.quickActions}
|
|
84
|
+
observer={false}
|
|
85
|
+
commentsEnabled
|
|
86
|
+
fileView
|
|
87
|
+
suspended={props.suspended}
|
|
88
|
+
onComposingChange={props.onComposingChange}
|
|
89
|
+
onAnnotate={(span, body) =>
|
|
90
|
+
setAnnotations((current) => [
|
|
91
|
+
...current,
|
|
92
|
+
{
|
|
93
|
+
id: newAnnotationId(),
|
|
94
|
+
kind: "comment",
|
|
95
|
+
anchor: makeAnchor(
|
|
96
|
+
diffRowBlocks(rows),
|
|
97
|
+
span.start.blockIndex,
|
|
98
|
+
span.start.char,
|
|
99
|
+
span.end.char,
|
|
100
|
+
span.end.blockIndex,
|
|
101
|
+
),
|
|
102
|
+
target: { kind: "welcome" },
|
|
103
|
+
body,
|
|
104
|
+
createdAt: new Date().toISOString(),
|
|
105
|
+
},
|
|
106
|
+
])
|
|
107
|
+
}
|
|
108
|
+
onReply={(rootAnnotationId, body) =>
|
|
109
|
+
setAnnotations((current) => {
|
|
110
|
+
const root = current.find((annotation) => annotation.id === rootAnnotationId);
|
|
111
|
+
|
|
112
|
+
if (!root) return current;
|
|
113
|
+
|
|
114
|
+
return [
|
|
115
|
+
...current,
|
|
116
|
+
{
|
|
117
|
+
id: newAnnotationId(),
|
|
118
|
+
kind: "comment",
|
|
119
|
+
anchor: root.anchor,
|
|
120
|
+
target: { kind: "welcome" },
|
|
121
|
+
body,
|
|
122
|
+
replyTo: rootAnnotationId,
|
|
123
|
+
createdAt: new Date().toISOString(),
|
|
124
|
+
},
|
|
125
|
+
];
|
|
126
|
+
})
|
|
127
|
+
}
|
|
128
|
+
onUpdateAnnotation={(id, body) =>
|
|
129
|
+
setAnnotations((current) =>
|
|
130
|
+
current.map((annotation) =>
|
|
131
|
+
annotation.id === id ? { ...annotation, body } : annotation,
|
|
132
|
+
),
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
onExit={() => {}}
|
|
136
|
+
theme={props.theme}
|
|
137
|
+
/>
|
|
138
|
+
);
|
|
139
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { ClientExtensionRegistry } from "../client-extension-registry";
|
|
3
|
+
import { DARK } from "../theme";
|
|
4
|
+
import type { Story, StoryMeta } from "./story";
|
|
5
|
+
import { WorkspacePanels } from "./WorkspacePanels";
|
|
6
|
+
|
|
7
|
+
export const meta: StoryMeta = { title: "Extensions/WorkspacePanels" };
|
|
8
|
+
|
|
9
|
+
const registry = new ClientExtensionRegistry();
|
|
10
|
+
|
|
11
|
+
void registry.load("example", (api) => {
|
|
12
|
+
api.registerView({
|
|
13
|
+
id: "details",
|
|
14
|
+
zone: "workspace.panels",
|
|
15
|
+
title: "Details",
|
|
16
|
+
Component: () => <text>Extension details</text>,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const RegisteredView: Story = {
|
|
21
|
+
render: () => (
|
|
22
|
+
<WorkspacePanels
|
|
23
|
+
width={32}
|
|
24
|
+
projectMode="tree"
|
|
25
|
+
projectPanel={<text>Project files</text>}
|
|
26
|
+
onToggleChanges={() => {}}
|
|
27
|
+
onToggleProject={() => {}}
|
|
28
|
+
onToggleRight={() => {}}
|
|
29
|
+
onFocus={() => {}}
|
|
30
|
+
registry={registry}
|
|
31
|
+
context={{ workspace: "/repo", threadId: "thread-1" }}
|
|
32
|
+
theme={DARK}
|
|
33
|
+
/>
|
|
34
|
+
),
|
|
35
|
+
size: { width: 32, height: 10 },
|
|
36
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from "react";
|
|
2
|
+
import { useKeyboard } from "@opentui/react";
|
|
3
|
+
import type { ExtensionUIContext } from "@cueloop/extension-api/client";
|
|
4
|
+
import type { ClientExtensionRegistry } from "../client-extension-registry";
|
|
5
|
+
import type { Theme } from "../theme";
|
|
6
|
+
import type { ProjectPanelMode } from "./AppShell";
|
|
7
|
+
import { ExtensionZone, useClientExtensionSnapshot, visibleContributions } from "./ExtensionZone";
|
|
8
|
+
import { FileTab, PanelColumn } from "./PanelColumn";
|
|
9
|
+
import { IconButton } from "./primitives/IconButton";
|
|
10
|
+
import { NERD } from "./primitives/icons";
|
|
11
|
+
|
|
12
|
+
/** The existing right pane owns built-in modes and selectable extension views. */
|
|
13
|
+
export function WorkspacePanels(props: {
|
|
14
|
+
width: number;
|
|
15
|
+
projectMode: ProjectPanelMode;
|
|
16
|
+
projectPanel: React.ReactNode;
|
|
17
|
+
onToggleChanges: () => void;
|
|
18
|
+
onToggleProject: () => void;
|
|
19
|
+
onToggleRight: () => void;
|
|
20
|
+
onFocus: () => void;
|
|
21
|
+
focused?: boolean;
|
|
22
|
+
registry: ClientExtensionRegistry;
|
|
23
|
+
context: ExtensionUIContext;
|
|
24
|
+
theme: Theme;
|
|
25
|
+
onExtensionError?: (message: string) => void;
|
|
26
|
+
}): React.ReactNode {
|
|
27
|
+
const { registry, context, theme, onExtensionError } = props;
|
|
28
|
+
const snapshot = useClientExtensionSnapshot(registry);
|
|
29
|
+
const { workspace, threadId } = context;
|
|
30
|
+
const visibility = useMemo(
|
|
31
|
+
() => visibleContributions(snapshot.views, { workspace, threadId }),
|
|
32
|
+
[snapshot.views, workspace, threadId],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
for (const error of visibility.errors) onExtensionError?.(error);
|
|
37
|
+
}, [visibility, onExtensionError]);
|
|
38
|
+
|
|
39
|
+
const views = visibility.items;
|
|
40
|
+
const [selectedView, setSelectedView] = useState<(typeof views)[number] | null>(null);
|
|
41
|
+
const selected = views.find(
|
|
42
|
+
({ key, value }) => key === selectedView?.key && value === selectedView.value,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const showBuiltIn = (toggle: () => void): void => {
|
|
46
|
+
setSelectedView(null);
|
|
47
|
+
toggle();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
useKeyboard((key) => {
|
|
51
|
+
if (!props.focused || (key.name !== "[" && key.name !== "]")) return;
|
|
52
|
+
const choices = [null, ...views];
|
|
53
|
+
const current = selected ? views.indexOf(selected) + 1 : 0;
|
|
54
|
+
const step = key.name === "]" ? 1 : -1;
|
|
55
|
+
|
|
56
|
+
setSelectedView(choices[(current + step + choices.length) % choices.length] ?? null);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const toggles = (
|
|
60
|
+
<box style={{ flexDirection: "row", flexShrink: 0, alignItems: "center" }}>
|
|
61
|
+
<IconButton
|
|
62
|
+
glyph="changes"
|
|
63
|
+
active={!selected && props.projectMode === "changes"}
|
|
64
|
+
onPress={() => showBuiltIn(props.onToggleChanges)}
|
|
65
|
+
marginRight={2}
|
|
66
|
+
theme={theme}
|
|
67
|
+
/>
|
|
68
|
+
<IconButton
|
|
69
|
+
glyph="project"
|
|
70
|
+
active={!selected && props.projectMode === "tree"}
|
|
71
|
+
onPress={() => showBuiltIn(props.onToggleProject)}
|
|
72
|
+
marginRight={2}
|
|
73
|
+
theme={theme}
|
|
74
|
+
/>
|
|
75
|
+
<IconButton
|
|
76
|
+
glyph={NERD.sidebarRight}
|
|
77
|
+
onPress={props.onToggleRight}
|
|
78
|
+
tip="Toggle Sidebar"
|
|
79
|
+
theme={theme}
|
|
80
|
+
/>
|
|
81
|
+
</box>
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<PanelColumn
|
|
86
|
+
width={props.width}
|
|
87
|
+
border="left"
|
|
88
|
+
header={
|
|
89
|
+
views.length > 0 ? (
|
|
90
|
+
<box style={{ flexDirection: "row", minWidth: 0 }}>
|
|
91
|
+
{views.map(({ key, value }) => (
|
|
92
|
+
<box key={key} onMouseUp={() => setSelectedView({ key, value })}>
|
|
93
|
+
<FileTab label={value.title} active={key === selected?.key} theme={theme} />
|
|
94
|
+
</box>
|
|
95
|
+
))}
|
|
96
|
+
</box>
|
|
97
|
+
) : null
|
|
98
|
+
}
|
|
99
|
+
headerRight={toggles}
|
|
100
|
+
onFocus={props.onFocus}
|
|
101
|
+
theme={theme}
|
|
102
|
+
>
|
|
103
|
+
{selected ? (
|
|
104
|
+
<ExtensionZone
|
|
105
|
+
zone="workspace.panels"
|
|
106
|
+
registry={registry}
|
|
107
|
+
context={context}
|
|
108
|
+
selectedViewId={selected.key}
|
|
109
|
+
onError={props.onExtensionError}
|
|
110
|
+
/>
|
|
111
|
+
) : (
|
|
112
|
+
props.projectPanel
|
|
113
|
+
)}
|
|
114
|
+
</PanelColumn>
|
|
115
|
+
);
|
|
116
|
+
}
|