@cueloop/client 0.1.0-alpha.10 → 0.1.0-alpha.101
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/annotations/annotation-palette.ts +62 -0
- package/src/annotations/components/AnnotationCards.stories.tsx +86 -0
- package/src/annotations/components/AnnotationCards.test.tsx +42 -0
- package/src/annotations/components/AnnotationCards.tsx +369 -0
- package/src/annotations/components/DiscussionMarkerRail.stories.tsx +54 -0
- package/src/annotations/components/DiscussionMarkerRail.tsx +186 -0
- package/src/annotations/discussions.ts +77 -0
- package/src/annotations/thread-selection.test.ts +210 -0
- package/src/annotations/thread-selection.ts +242 -0
- package/src/annotations/use-annotation-surface.tsx +1243 -0
- package/src/app/App.appearance.test.tsx +53 -0
- package/src/{App.autoclose.test.tsx → app/App.autoclose.test.tsx} +77 -51
- package/src/app/App.deleted-scroll.test.tsx +187 -0
- package/src/app/App.diff.test.tsx +313 -0
- package/src/app/App.export.test.tsx +105 -0
- package/src/app/App.focus.test.ts +89 -0
- package/src/app/App.identity.test.tsx +84 -0
- package/src/app/App.plan-render.test.tsx +209 -0
- package/src/app/App.plan-review.test.tsx +428 -0
- package/src/app/App.prototype-markdown.test.tsx +128 -0
- package/src/app/App.prototype.test.tsx +176 -0
- package/src/app/App.readonly.test.tsx +210 -0
- package/src/app/App.regression.test.tsx +99 -0
- package/src/app/App.rendering.test.tsx +134 -0
- package/src/app/App.share-dialog.test.tsx +177 -0
- package/src/app/App.submit-confirm.test.tsx +267 -0
- package/src/app/App.test.tsx +496 -0
- package/src/app/App.thread-agent.test.ts +20 -0
- package/src/app/App.toast.test.tsx +124 -0
- package/src/app/App.tsx +1667 -0
- package/src/app/App.visual.test.tsx +59 -0
- package/src/app/App.walk.test.tsx +299 -0
- package/src/app/App.workbench.test.tsx +1149 -0
- package/src/app/app-key-state.ts +61 -0
- package/src/app/app-screens.tsx +606 -0
- package/src/app/app-view-model.ts +168 -0
- package/src/app/components/AppHeader.stories.tsx +50 -0
- package/src/app/components/AppHeader.tsx +81 -0
- package/src/app/components/AppShell.brand.test.tsx +58 -0
- package/src/app/components/AppShell.extensions.test.tsx +191 -0
- package/src/app/components/AppShell.stories.tsx +89 -0
- package/src/app/components/AppShell.tsx +358 -0
- package/src/app/components/AppShell.zoom.test.tsx +149 -0
- package/src/app/components/Breadcrumb.stories.tsx +19 -0
- package/src/app/components/Breadcrumb.tsx +45 -0
- package/src/app/components/MenuBar.stories.tsx +28 -0
- package/src/app/components/MenuBar.tsx +44 -0
- package/src/app/components/PaneGrid.stories.tsx +84 -0
- package/src/app/components/PaneGrid.tsx +128 -0
- package/src/app/components/PanelColumn.stories.tsx +25 -0
- package/src/app/components/PanelColumn.tsx +93 -0
- package/src/app/components/ShellHeader.stories.tsx +50 -0
- package/src/app/components/ShellHeader.tsx +137 -0
- package/src/app/components/WorkspacePanels.stories.tsx +36 -0
- package/src/app/components/WorkspacePanels.tsx +120 -0
- package/src/app/components/pages/Workspace.stories.tsx +181 -0
- package/src/app/intent-dispatch.test.ts +728 -0
- package/src/app/intent-dispatch.ts +687 -0
- package/src/app/launch-layout.test.ts +78 -0
- package/src/app/launch-layout.ts +57 -0
- package/src/app/ready-signal.test.ts +85 -0
- package/src/app/ready-signal.ts +52 -0
- package/src/app/run.ts +109 -0
- package/src/{serve.test.ts → app/serve.test.ts} +24 -12
- package/src/app/serve.ts +111 -0
- package/src/app/version.ts +5 -0
- package/src/appearance/components/theme-context.stories.tsx +46 -0
- package/src/appearance/components/theme-context.tsx +37 -0
- package/src/appearance/theme-presets.test.ts +51 -0
- package/src/appearance/theme-presets.ts +219 -0
- package/src/appearance/theme.ts +112 -0
- package/src/appearance/visual/contrast.test.ts +35 -0
- package/src/appearance/visual/contrast.ts +33 -0
- package/src/diff/components/AnnotatableFileView.stories.tsx +42 -0
- package/src/diff/components/AnnotatableFileView.test.tsx +157 -0
- package/src/diff/components/AnnotatableFileView.tsx +141 -0
- package/src/diff/components/BareWorkbenchFileView.stories.tsx +27 -0
- package/src/diff/components/BareWorkbenchFileView.tsx +63 -0
- package/src/diff/components/DiffContentView.scroll.test.tsx +434 -0
- package/src/diff/components/DiffContentView.split.test.tsx +140 -0
- package/src/diff/components/DiffContentView.stories.tsx +80 -0
- package/src/diff/components/DiffContentView.syntax.test.tsx +82 -0
- package/src/diff/components/DiffContentView.tsx +1055 -0
- package/src/diff/components/WalkWizard.stories.tsx +100 -0
- package/src/diff/components/WalkWizard.tsx +146 -0
- package/src/diff/components/diff-content-view-layout.test.ts +198 -0
- package/src/diff/components/diff-content-view-layout.ts +84 -0
- package/src/diff/diff-fold.test.ts +78 -0
- package/src/diff/diff-fold.ts +126 -0
- package/src/diff/diff-hunk-curate.test.ts +315 -0
- package/src/diff/diff-hunk-curate.ts +152 -0
- package/src/diff/diff-intraline.test.ts +239 -0
- package/src/diff/diff-intraline.ts +238 -0
- package/src/diff/diff-syntax.test.ts +125 -0
- package/src/diff/diff-syntax.ts +186 -0
- package/src/diff/split-diff.test.ts +110 -0
- package/src/diff/split-diff.ts +144 -0
- package/src/diff/view-diff.test.ts +325 -0
- package/src/diff/view-diff.ts +522 -0
- package/src/diff/walk.test.ts +121 -0
- package/src/diff/walk.ts +73 -0
- package/src/index.ts +31 -2
- package/src/integrations/attribution.test.ts +31 -0
- package/src/integrations/attribution.ts +42 -0
- package/src/integrations/client-extension-registry.test.tsx +61 -0
- package/src/integrations/client-extension-registry.ts +159 -0
- package/src/integrations/clipboard.test.ts +23 -0
- package/src/integrations/clipboard.ts +33 -0
- package/src/integrations/components/ExtensionZone.stories.tsx +28 -0
- package/src/integrations/components/ExtensionZone.tsx +145 -0
- package/src/integrations/components/ShareDialog.agent.test.tsx +50 -0
- package/src/integrations/components/ShareDialog.stories.tsx +121 -0
- package/src/integrations/components/ShareDialog.tsx +574 -0
- package/src/integrations/components/share-dialog-store.ts +59 -0
- package/src/integrations/editor.test.ts +149 -0
- package/src/integrations/editor.ts +160 -0
- package/src/integrations/github-identity.test.ts +58 -0
- package/src/integrations/github-identity.ts +41 -0
- package/src/integrations/herdr.test.ts +43 -0
- package/src/integrations/herdr.ts +38 -0
- package/src/integrations/integrations.test.ts +41 -0
- package/src/integrations/integrations.ts +32 -0
- package/src/integrations/pasted-image.test.ts +67 -0
- package/src/integrations/pasted-image.ts +60 -0
- package/src/integrations/share.ts +238 -0
- package/src/keyboard/components/CompletionOverlay.stories.tsx +37 -0
- package/src/keyboard/components/CompletionOverlay.tsx +75 -0
- package/src/keyboard/components/HandleChipsInput.stories.tsx +24 -0
- package/src/keyboard/components/HandleChipsInput.test.tsx +35 -0
- package/src/keyboard/components/HandleChipsInput.tsx +143 -0
- package/src/keyboard/components/KeybindsDialog.stories.tsx +31 -0
- package/src/keyboard/components/KeybindsDialog.tsx +103 -0
- package/src/keyboard/components/NavModeHint.stories.tsx +40 -0
- package/src/keyboard/components/NavModeHint.tsx +31 -0
- package/src/keyboard/components/SlashComposer.stories.tsx +37 -0
- package/src/keyboard/components/SlashComposer.tsx +87 -0
- package/src/keyboard/key-bindings.test.ts +156 -0
- package/src/keyboard/key-bindings.ts +459 -0
- package/src/keyboard/keymap.test.ts +611 -0
- package/src/keyboard/keymap.ts +395 -0
- package/src/keyboard/skills.test.ts +74 -0
- package/src/keyboard/skills.ts +75 -0
- package/src/keyboard/slash-palette.test.ts +103 -0
- package/src/keyboard/slash-palette.ts +129 -0
- package/src/keyboard/thread-chords.test.ts +79 -0
- package/src/keyboard/thread-chords.ts +163 -0
- package/src/keyboard/use-shared-keyboard.ts +55 -0
- package/src/markdown/__snapshots__/view-plan.regression.test.ts.snap +100 -0
- package/src/markdown/components/MarkdownGridBlock.stories.tsx +33 -0
- package/src/markdown/components/MarkdownGridBlock.tsx +72 -0
- package/src/markdown/components/MarkdownThreadEditor.stories.tsx +15 -0
- package/src/markdown/components/MarkdownThreadEditor.test.tsx +98 -0
- package/src/markdown/components/MarkdownThreadEditor.tsx +160 -0
- package/src/markdown/components/ThreadView.selection.test.tsx +951 -0
- package/src/markdown/components/ThreadView.slash.test.tsx +146 -0
- package/src/markdown/components/ThreadView.stories.tsx +26 -0
- package/src/markdown/components/ThreadView.tsx +560 -0
- package/src/markdown/mark-runs.ts +121 -0
- package/src/markdown/markdown-editor-syntax-style.ts +50 -0
- package/src/markdown/markdown-grid.test.ts +101 -0
- package/src/markdown/markdown-grid.ts +203 -0
- package/src/markdown/markdown-highlight.test.ts +61 -0
- package/src/markdown/markdown-highlight.ts +124 -0
- package/src/markdown/thread-code-syntax.ts +45 -0
- package/src/markdown/use-thread-body-editing.tsx +54 -0
- package/src/markdown/view-plan.regression.test.ts +60 -0
- package/src/markdown/view-plan.test.ts +545 -0
- package/src/markdown/view-plan.ts +1009 -0
- package/src/performance/otlp-trace.test.ts +119 -0
- package/src/performance/otlp-trace.ts +134 -0
- package/src/performance/perf-report.ts +18 -0
- package/src/performance/perf-timings.test.ts +18 -0
- package/src/performance/perf-timings.ts +39 -0
- package/src/prototype/components/PrototypeContentView.stories.tsx +19 -0
- package/src/prototype/components/PrototypeContentView.tsx +442 -0
- package/src/prototype/prototype-browser.test.ts +81 -0
- package/src/prototype/prototype-browser.ts +321 -0
- package/src/prototype/prototype-pixels.tsx +35 -0
- package/src/settings/components/SettingsDialog.stories.tsx +117 -0
- package/src/settings/components/SettingsDialog.tsx +183 -0
- package/src/settings/components/SettingsRows.stories.tsx +18 -0
- package/src/settings/components/SettingsRows.tsx +112 -0
- package/src/settings/components/quick-actions-editor.stories.tsx +58 -0
- package/src/settings/components/quick-actions-editor.test.tsx +120 -0
- package/src/settings/components/quick-actions-editor.tsx +222 -0
- package/src/settings/config-identity.test.ts +78 -0
- package/src/settings/config.test.ts +676 -0
- package/src/settings/config.ts +628 -0
- package/src/settings/use-settings-dialog.tsx +351 -0
- package/src/stories/__snapshots__/stories.test.tsx.snap +2358 -0
- package/src/stories/stories-app.tsx +196 -0
- package/src/stories/stories.test.tsx +74 -0
- package/src/stories/story-fixtures.ts +102 -0
- package/src/stories/story-tree.test.ts +108 -0
- package/src/stories/story.ts +154 -0
- package/src/terminal/ansi-screen.test.ts +28 -0
- package/src/terminal/ansi-screen.ts +72 -0
- package/src/terminal/components/AnsiScreen.stories.tsx +12 -0
- package/src/terminal/components/AnsiScreen.tsx +28 -0
- package/src/terminal/components/terminal-pane.test.tsx +40 -0
- package/src/terminal/components/terminal-pane.ts +196 -0
- package/src/terminal/ghostty-terminal.test.ts +85 -0
- package/src/terminal/ghostty-terminal.ts +197 -0
- package/src/terminal/kitty-image.test.ts +66 -0
- package/src/terminal/kitty-image.ts +137 -0
- package/src/terminal/pty.test.ts +81 -0
- package/src/terminal/pty.ts +246 -0
- package/src/testing/test-support.test.ts +264 -0
- package/src/testing/test-support.ts +367 -0
- package/src/thread/agent-continuation.test.ts +32 -0
- package/src/thread/agent-continuation.ts +13 -0
- package/src/thread/agent-transcript.test.ts +161 -0
- package/src/thread/agent-transcript.ts +336 -0
- package/src/thread/components/AgentConfigControls.stories.tsx +39 -0
- package/src/thread/components/AgentConfigControls.test.tsx +112 -0
- package/src/thread/components/AgentConfigControls.tsx +114 -0
- package/src/thread/components/AgentThreadPane.stories.tsx +105 -0
- package/src/thread/components/AgentThreadPane.test.tsx +935 -0
- package/src/thread/components/AgentThreadPane.tsx +377 -0
- package/src/thread/components/ThreadFooter.stories.tsx +30 -0
- package/src/thread/components/ThreadFooter.test.tsx +49 -0
- package/src/thread/components/ThreadFooter.tsx +77 -0
- package/src/thread/components/ThreadTree.stories.tsx +46 -0
- package/src/thread/components/ThreadTree.test.tsx +41 -0
- package/src/thread/components/ThreadTree.tsx +267 -0
- package/src/thread/components/thread-tree.test.ts +137 -0
- package/src/thread/components/thread-tree.ts +112 -0
- package/src/thread/thread-controller.curate.test.ts +502 -0
- package/src/thread/thread-controller.derivation.test.ts +62 -0
- package/src/thread/thread-controller.live-diff.test.ts +357 -0
- package/src/thread/thread-controller.optimistic.test.ts +69 -0
- package/src/thread/thread-controller.share.test.ts +573 -0
- package/src/thread/thread-controller.switch-cache.test.ts +174 -0
- package/src/thread/thread-controller.tree.test.ts +263 -0
- package/src/thread/thread-controller.ts +2026 -0
- package/src/thread/tree-view.test.ts +128 -0
- package/src/thread/tree-view.ts +146 -0
- package/src/thread/use-thread-agent.test.tsx +208 -0
- package/src/thread/use-thread-agent.ts +117 -0
- package/src/ui/components/CodeBlock.stories.tsx +42 -0
- package/src/ui/components/CodeBlock.tsx +80 -0
- package/src/ui/components/ConfirmCard.stories.tsx +37 -0
- package/src/ui/components/ConfirmCard.tsx +151 -0
- package/src/ui/components/ConfirmDialog.stories.tsx +21 -0
- package/src/ui/components/ConfirmDialog.tsx +72 -0
- package/src/ui/components/OverlayScrollbar.stories.tsx +33 -0
- package/src/ui/components/OverlayScrollbar.test.tsx +139 -0
- package/src/ui/components/OverlayScrollbar.tsx +129 -0
- package/src/ui/components/PromptDialog.stories.tsx +36 -0
- package/src/ui/components/PromptDialog.test.tsx +65 -0
- package/src/ui/components/PromptDialog.tsx +99 -0
- package/src/ui/components/RootOverlay.stories.tsx +45 -0
- package/src/ui/components/RootOverlay.tsx +49 -0
- package/src/ui/components/ScrollArea.stories.tsx +20 -0
- package/src/ui/components/ScrollArea.tsx +61 -0
- package/src/ui/components/Toast.stories.tsx +19 -0
- package/src/ui/components/Toast.tsx +54 -0
- package/src/ui/components/Tooltip.stories.tsx +26 -0
- package/src/ui/components/Tooltip.tsx +72 -0
- package/src/ui/components/menu-control.stories.tsx +35 -0
- package/src/ui/components/menu-control.tsx +43 -0
- package/src/ui/components/primitives/Button.stories.tsx +32 -0
- package/src/ui/components/primitives/Button.tsx +57 -0
- package/src/ui/components/primitives/Card.stories.tsx +25 -0
- package/src/ui/components/primitives/Card.tsx +62 -0
- package/src/ui/components/primitives/Dialog.stories.tsx +24 -0
- package/src/ui/components/primitives/Dialog.test.tsx +38 -0
- package/src/ui/components/primitives/Dialog.tsx +69 -0
- package/src/ui/components/primitives/DialogActions.stories.tsx +20 -0
- package/src/ui/components/primitives/DialogActions.tsx +39 -0
- package/src/ui/components/primitives/IconButton.stories.tsx +36 -0
- package/src/ui/components/primitives/IconButton.tsx +58 -0
- package/src/ui/components/primitives/StatusBar.stories.tsx +11 -0
- package/src/ui/components/primitives/StatusBar.tsx +23 -0
- package/src/ui/components/primitives/Switch.stories.tsx +17 -0
- package/src/ui/components/primitives/Switch.tsx +32 -0
- package/src/ui/components/primitives/Tabs.stories.tsx +29 -0
- package/src/ui/components/primitives/Tabs.tsx +101 -0
- package/src/ui/components/primitives/Toolbar.stories.tsx +17 -0
- package/src/ui/components/primitives/Toolbar.tsx +14 -0
- package/src/ui/components/primitives/Tree.stories.tsx +124 -0
- package/src/ui/components/primitives/Tree.tsx +133 -0
- package/src/ui/components/primitives/frame.test.ts +12 -0
- package/src/ui/components/primitives/frame.ts +17 -0
- package/src/ui/components/primitives/icons.ts +61 -0
- package/src/ui/components/primitives/tree-model.test.ts +99 -0
- package/src/ui/components/primitives/tree-model.ts +137 -0
- package/src/ui/components/syntax-highlight.ts +133 -0
- package/src/ui/components/truncate-text.ts +7 -0
- package/src/ui/components/truncate-title.test.ts +31 -0
- package/src/ui/components/truncate-title.ts +15 -0
- package/src/ui/use-frame-measure.ts +74 -0
- package/src/ui/use-terminal-virtualizer.ts +259 -0
- package/src/workbench/components/ChangesColumn.stories.tsx +44 -0
- package/src/workbench/components/ChangesColumn.tsx +203 -0
- package/src/workbench/components/ChangesFileTree.test.tsx +181 -0
- package/src/workbench/components/EditorGrid.split.test.tsx +118 -0
- package/src/workbench/components/EditorGrid.stories.tsx +86 -0
- package/src/workbench/components/EditorGrid.tabs.test.tsx +118 -0
- package/src/workbench/components/EditorGrid.tsx +544 -0
- package/src/workbench/components/EditorGrid.zoom.test.tsx +132 -0
- package/src/workbench/components/FileContentsView.stories.tsx +16 -0
- package/src/workbench/components/FileContentsView.test.tsx +29 -0
- package/src/workbench/components/FileContentsView.tsx +100 -0
- package/src/workbench/components/GridTabContent.actions.test.tsx +151 -0
- package/src/workbench/components/GridTabContent.stories.tsx +59 -0
- package/src/workbench/components/GridTabContent.tsx +193 -0
- package/src/workbench/components/ProjectTreeView.stories.tsx +25 -0
- package/src/workbench/components/ProjectTreeView.test.tsx +236 -0
- package/src/workbench/components/ProjectTreeView.tsx +151 -0
- package/src/workbench/components/TreePane.stories.tsx +106 -0
- package/src/workbench/components/TreePane.tsx +155 -0
- package/src/workbench/components/WelcomePlayground.stories.tsx +12 -0
- package/src/workbench/components/WelcomePlayground.test.tsx +83 -0
- package/src/workbench/components/WelcomePlayground.tsx +138 -0
- package/src/workbench/components/editor-grid.test.ts +184 -0
- package/src/workbench/components/editor-grid.ts +281 -0
- package/src/workbench/components/file-tree.test.ts +60 -0
- package/src/workbench/components/file-tree.ts +112 -0
- package/src/workbench/use-changes-workbench.ts +205 -0
- package/src/workbench/use-remember-layout.ts +31 -0
- package/src/workbench/use-repo-changes.ts +91 -0
- package/src/workbench/workbench-snapshot.test.ts +99 -0
- package/src/workbench/workbench-snapshot.ts +59 -0
- package/src/App.diff.test.tsx +0 -99
- package/src/App.export.test.tsx +0 -88
- package/src/App.plan-review.test.tsx +0 -259
- package/src/App.readonly.test.tsx +0 -148
- package/src/App.rendering.test.tsx +0 -108
- package/src/App.submit-confirm.test.tsx +0 -183
- package/src/App.test.tsx +0 -218
- package/src/App.tsx +0 -1249
- package/src/config.test.ts +0 -116
- package/src/config.ts +0 -150
- package/src/editor.ts +0 -34
- package/src/herdr.test.ts +0 -68
- package/src/herdr.ts +0 -34
- package/src/keymap.test.ts +0 -270
- package/src/keymap.ts +0 -173
- package/src/run.ts +0 -30
- package/src/serve.ts +0 -75
- package/src/session-controller.ts +0 -398
- package/src/syntax.test.ts +0 -41
- package/src/syntax.ts +0 -88
- package/src/theme.ts +0 -46
- package/src/view-diff.test.ts +0 -55
- package/src/view-diff.ts +0 -86
- package/src/view.test.ts +0 -157
- package/src/view.ts +0 -364
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Build libcuelooppty: the flat forkpty(3) FFI shim in src/pty.zig. Pure libc, so
|
|
3
|
+
# unlike build.sh this needs no Ghostty clone - just the pinned Zig toolchain.
|
|
4
|
+
# Output lands in native/<os>-<arch>/libcuelooppty.{dylib,so}. Requires curl.
|
|
5
|
+
set -eu
|
|
6
|
+
|
|
7
|
+
ZIG_VERSION="0.16.0"
|
|
8
|
+
|
|
9
|
+
here="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
work="${TMPDIR:-/tmp}/cueloop-libghostty-build"
|
|
11
|
+
mkdir -p "$work"
|
|
12
|
+
export ZIG_GLOBAL_CACHE_DIR="$work/zig-gcache"
|
|
13
|
+
export ZIG_LOCAL_CACHE_DIR="$work/zig-lcache"
|
|
14
|
+
cd "$work"
|
|
15
|
+
|
|
16
|
+
arch="$(uname -m)"; os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
17
|
+
case "$os" in darwin) zig_os="macos"; dylib="libcuelooppty.dylib";; linux) zig_os="linux"; dylib="libcuelooppty.so";; *) echo "unsupported os $os" >&2; exit 1;; esac
|
|
18
|
+
case "$arch-$os" in
|
|
19
|
+
arm64-darwin|aarch64-linux) zig_arch="aarch64"; bun_arch="arm64";;
|
|
20
|
+
x86_64-darwin|x86_64-linux) zig_arch="x86_64"; bun_arch="x64";;
|
|
21
|
+
*) echo "unsupported architecture $arch on $os" >&2; exit 1;;
|
|
22
|
+
esac
|
|
23
|
+
case "$zig_arch-$zig_os" in
|
|
24
|
+
aarch64-macos) zig_sha="b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489";;
|
|
25
|
+
x86_64-macos) zig_sha="0387557ed1877bc6a2e1802c8391953baddba76081876301c522f52977b52ba7";;
|
|
26
|
+
aarch64-linux) zig_sha="ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17";;
|
|
27
|
+
x86_64-linux) zig_sha="70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00";;
|
|
28
|
+
esac
|
|
29
|
+
outdir="$here/${os}-${bun_arch}"
|
|
30
|
+
zig_archive="zig-$zig_arch-$zig_os-$ZIG_VERSION.tar.xz"
|
|
31
|
+
|
|
32
|
+
if [ ! -f "$zig_archive" ]; then
|
|
33
|
+
echo "== fetching zig $ZIG_VERSION =="
|
|
34
|
+
curl -fsSL -o "$zig_archive" "https://ziglang.org/download/$ZIG_VERSION/$zig_archive"
|
|
35
|
+
fi
|
|
36
|
+
printf '%s %s\n' "$zig_sha" "$zig_archive" | shasum -a 256 -c -
|
|
37
|
+
|
|
38
|
+
build_root="$(mktemp -d "$work/native-pty-build.XXXXXX")"
|
|
39
|
+
trap 'rm -rf "$build_root"' EXIT HUP INT TERM
|
|
40
|
+
tar -xf "$zig_archive" -C "$build_root"
|
|
41
|
+
zig="$build_root/zig-$zig_arch-$zig_os-$ZIG_VERSION/zig"
|
|
42
|
+
|
|
43
|
+
echo "== building pty shim -> $outdir/$dylib =="
|
|
44
|
+
mkdir -p "$outdir"
|
|
45
|
+
if [ "$os" = "darwin" ]; then
|
|
46
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" \
|
|
47
|
+
-install_name "@rpath/$dylib" "$here/src/pty.zig"
|
|
48
|
+
else
|
|
49
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" "$here/src/pty.zig"
|
|
50
|
+
fi
|
|
51
|
+
echo "== done: $outdir/$dylib =="
|
package/native/build.sh
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Rebuild libcueloopvt.dylib: libghostty-vt (Ghostty's VT core) statically linked
|
|
3
|
+
# behind the flat FFI shim. Pinned to a Ghostty commit + Zig 0.16.0.
|
|
4
|
+
# Output lands in native/<os>-<arch>/libcueloopvt.dylib. Requires curl and git.
|
|
5
|
+
set -eu
|
|
6
|
+
|
|
7
|
+
GHOSTTY_COMMIT="5834a0e3df621802e9578e4562d88b0c2ad4ada8"
|
|
8
|
+
ZIG_VERSION="0.16.0"
|
|
9
|
+
|
|
10
|
+
here="$(cd "$(dirname "$0")" && pwd)"
|
|
11
|
+
work="${TMPDIR:-/tmp}/cueloop-libghostty-build"
|
|
12
|
+
mkdir -p "$work"
|
|
13
|
+
export ZIG_GLOBAL_CACHE_DIR="$work/zig-gcache"
|
|
14
|
+
export ZIG_LOCAL_CACHE_DIR="$work/zig-lcache"
|
|
15
|
+
cd "$work"
|
|
16
|
+
|
|
17
|
+
# Host triple for the Bun prebuilt dir and Zig download. The two tools use
|
|
18
|
+
# different architecture names.
|
|
19
|
+
arch="$(uname -m)"; os="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
20
|
+
case "$os" in darwin) zig_os="macos"; dylib="libcueloopvt.dylib";; linux) zig_os="linux"; dylib="libcueloopvt.so";; *) echo "unsupported os $os" >&2; exit 1;; esac
|
|
21
|
+
case "$arch-$os" in
|
|
22
|
+
arm64-darwin|aarch64-linux) zig_arch="aarch64"; bun_arch="arm64";;
|
|
23
|
+
x86_64-darwin|x86_64-linux) zig_arch="x86_64"; bun_arch="x64";;
|
|
24
|
+
*) echo "unsupported architecture $arch on $os" >&2; exit 1;;
|
|
25
|
+
esac
|
|
26
|
+
case "$zig_arch-$zig_os" in
|
|
27
|
+
aarch64-macos) zig_sha="b23d70deaa879b5c2d486ed3316f7eaa53e84acf6fc9cc747de152450d401489";;
|
|
28
|
+
x86_64-macos) zig_sha="0387557ed1877bc6a2e1802c8391953baddba76081876301c522f52977b52ba7";;
|
|
29
|
+
aarch64-linux) zig_sha="ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17";;
|
|
30
|
+
x86_64-linux) zig_sha="70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00";;
|
|
31
|
+
esac
|
|
32
|
+
outdir="$here/${os}-${bun_arch}"
|
|
33
|
+
zig_archive="zig-$zig_arch-$zig_os-$ZIG_VERSION.tar.xz"
|
|
34
|
+
|
|
35
|
+
if [ ! -f "$zig_archive" ]; then
|
|
36
|
+
echo "== fetching zig $ZIG_VERSION =="
|
|
37
|
+
curl -fsSL -o "$zig_archive" "https://ziglang.org/download/$ZIG_VERSION/$zig_archive"
|
|
38
|
+
fi
|
|
39
|
+
printf '%s %s\n' "$zig_sha" "$zig_archive" | shasum -a 256 -c -
|
|
40
|
+
|
|
41
|
+
if [ ! -d ghostty ]; then
|
|
42
|
+
echo "== cloning ghostty @ $GHOSTTY_COMMIT =="
|
|
43
|
+
mkdir ghostty && cd ghostty && git init -q
|
|
44
|
+
git remote add origin https://github.com/ghostty-org/ghostty.git
|
|
45
|
+
cd ..
|
|
46
|
+
fi
|
|
47
|
+
if ! git -C ghostty cat-file -e "$GHOSTTY_COMMIT^{commit}" 2>/dev/null; then
|
|
48
|
+
git -C ghostty fetch -q --depth 1 origin "$GHOSTTY_COMMIT"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Build only from fresh trees materialized from the verified archive and exact
|
|
52
|
+
# Git object. Cached working-tree edits and stale install outputs cannot enter.
|
|
53
|
+
build_root="$(mktemp -d "$work/native-build.XXXXXX")"
|
|
54
|
+
trap 'rm -rf "$build_root"' EXIT HUP INT TERM
|
|
55
|
+
tar -xf "$zig_archive" -C "$build_root"
|
|
56
|
+
zig="$build_root/zig-$zig_arch-$zig_os-$ZIG_VERSION/zig"
|
|
57
|
+
ghostty_source="$build_root/ghostty"
|
|
58
|
+
mkdir "$ghostty_source"
|
|
59
|
+
git -C ghostty archive "$GHOSTTY_COMMIT" | tar -x -C "$ghostty_source"
|
|
60
|
+
|
|
61
|
+
echo "== building libghostty-vt (ReleaseFast) =="
|
|
62
|
+
cd "$ghostty_source"
|
|
63
|
+
# The final xcframework packaging step can fail while the .a we need is produced,
|
|
64
|
+
# so tolerate a non-zero exit here but assert the static lib exists below.
|
|
65
|
+
vt_install="$build_root/vt-install"
|
|
66
|
+
"$zig" build -Demit-lib-vt -Doptimize=ReleaseFast --prefix "$vt_install" || true
|
|
67
|
+
cd ..
|
|
68
|
+
staticlib="$vt_install/lib/libghostty-vt.a"
|
|
69
|
+
[ -f "$staticlib" ] || { echo "build failed: $staticlib not produced" >&2; exit 1; }
|
|
70
|
+
|
|
71
|
+
echo "== linking shim -> $outdir/$dylib =="
|
|
72
|
+
mkdir -p "$outdir"
|
|
73
|
+
if [ "$os" = "darwin" ]; then
|
|
74
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" \
|
|
75
|
+
-install_name "@rpath/$dylib" \
|
|
76
|
+
-I"$vt_install/include" "$here/src/shim.zig" "$staticlib" \
|
|
77
|
+
-framework CoreFoundation -framework CoreText -framework CoreGraphics
|
|
78
|
+
else
|
|
79
|
+
"$zig" build-lib -dynamic -OReleaseFast -lc -femit-bin="$outdir/$dylib" \
|
|
80
|
+
-I"$vt_install/include" "$here/src/shim.zig" "$staticlib" -lm -lpthread
|
|
81
|
+
fi
|
|
82
|
+
echo "== done: $outdir/$dylib =="
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Flat, FFI-trivial pseudo-terminal over libc forkpty(3). Exposes exactly what
|
|
2
|
+
// the embedded Agent-tab terminal needs: spawn a child on a PTY, non-blocking
|
|
3
|
+
// read of its output, write, resize, and reap. Handles are small ints into a
|
|
4
|
+
// fixed table so bun:ffi only ever passes ints and byte pointers, never structs.
|
|
5
|
+
//
|
|
6
|
+
// Packed-argument contract (avoids shell parsing and per-token FFI marshalling):
|
|
7
|
+
// argv_packed: "arg0\0arg1\0...\0" - NUL-separated, terminated by an empty
|
|
8
|
+
// token (a trailing extra NUL).
|
|
9
|
+
// env_packed: "K=V\0K=V\0...\0" - same shape; empty means inherit parent.
|
|
10
|
+
// cwd: a plain C string, or empty to keep the parent's directory.
|
|
11
|
+
|
|
12
|
+
const std = @import("std");
|
|
13
|
+
const builtin = @import("builtin");
|
|
14
|
+
|
|
15
|
+
const c = @cImport({
|
|
16
|
+
@cInclude("unistd.h");
|
|
17
|
+
@cInclude("fcntl.h");
|
|
18
|
+
@cInclude("errno.h");
|
|
19
|
+
@cInclude("signal.h");
|
|
20
|
+
@cInclude("stdlib.h");
|
|
21
|
+
@cInclude("string.h");
|
|
22
|
+
@cInclude("termios.h");
|
|
23
|
+
@cInclude("sys/ioctl.h");
|
|
24
|
+
@cInclude("sys/wait.h");
|
|
25
|
+
if (builtin.os.tag == .macos) @cInclude("util.h") else @cInclude("pty.h");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
extern var environ: [*c][*c]u8;
|
|
29
|
+
|
|
30
|
+
const MAX_SESSIONS = 64;
|
|
31
|
+
const MAX_TOKENS = 1024;
|
|
32
|
+
|
|
33
|
+
const PtySession = struct {
|
|
34
|
+
in_use: bool = false,
|
|
35
|
+
master_fd: c_int = -1,
|
|
36
|
+
pid: c.pid_t = 0,
|
|
37
|
+
reaped: bool = false,
|
|
38
|
+
exit_code: c_int = 0,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
var sessions = [_]PtySession{.{}} ** MAX_SESSIONS;
|
|
42
|
+
|
|
43
|
+
// Split a double-NUL-terminated packed buffer into a NULL-terminated array of
|
|
44
|
+
// pointers into that same buffer. Returns the token count, or -1 on overflow.
|
|
45
|
+
fn splitPacked(packed_buffer: [*c]u8, out: [][*c]u8) c_int {
|
|
46
|
+
var count: usize = 0;
|
|
47
|
+
var cursor: [*c]u8 = packed_buffer;
|
|
48
|
+
while (cursor[0] != 0) {
|
|
49
|
+
if (count >= out.len - 1) return -1;
|
|
50
|
+
out[count] = cursor;
|
|
51
|
+
count += 1;
|
|
52
|
+
cursor += c.strlen(cursor) + 1;
|
|
53
|
+
}
|
|
54
|
+
out[count] = null;
|
|
55
|
+
return @intCast(count);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Reap the child once, recording its exit status. WNOHANG unless `block`.
|
|
59
|
+
fn reapSession(session: *PtySession, block: bool) void {
|
|
60
|
+
if (session.reaped) return;
|
|
61
|
+
var status: c_int = 0;
|
|
62
|
+
const result = c.waitpid(session.pid, &status, if (block) 0 else c.WNOHANG);
|
|
63
|
+
if (result != session.pid) return;
|
|
64
|
+
session.reaped = true;
|
|
65
|
+
if (c.WIFEXITED(status)) {
|
|
66
|
+
session.exit_code = c.WEXITSTATUS(status);
|
|
67
|
+
} else if (c.WIFSIGNALED(status)) {
|
|
68
|
+
session.exit_code = 128 + c.WTERMSIG(status);
|
|
69
|
+
} else {
|
|
70
|
+
session.exit_code = -1;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn sessionOf(handle: c_int) ?*PtySession {
|
|
75
|
+
if (handle < 0 or handle >= MAX_SESSIONS) return null;
|
|
76
|
+
const session = &sessions[@intCast(handle)];
|
|
77
|
+
if (!session.in_use) return null;
|
|
78
|
+
return session;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Spawn `argv_packed` on a fresh PTY sized cols x rows, in cwd, with env_packed
|
|
82
|
+
// (or the parent's env when empty). Returns a session handle, or -1 on failure.
|
|
83
|
+
export fn cueloop_pty_spawn(argv_packed: [*c]u8, cwd: [*c]const u8, env_packed: [*c]u8, cols: c_int, rows: c_int) c_int {
|
|
84
|
+
var handle: c_int = -1;
|
|
85
|
+
for (0..MAX_SESSIONS) |index| {
|
|
86
|
+
if (!sessions[index].in_use) {
|
|
87
|
+
handle = @intCast(index);
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (handle < 0) return -1;
|
|
92
|
+
|
|
93
|
+
var argv: [MAX_TOKENS][*c]u8 = undefined;
|
|
94
|
+
if (splitPacked(argv_packed, &argv) < 1) return -1;
|
|
95
|
+
|
|
96
|
+
var envp: [MAX_TOKENS][*c]u8 = undefined;
|
|
97
|
+
const have_env = env_packed != null and env_packed[0] != 0;
|
|
98
|
+
if (have_env and splitPacked(env_packed, &envp) < 0) return -1;
|
|
99
|
+
|
|
100
|
+
var winsize = std.mem.zeroes(c.struct_winsize);
|
|
101
|
+
winsize.ws_col = @intCast(cols);
|
|
102
|
+
winsize.ws_row = @intCast(rows);
|
|
103
|
+
|
|
104
|
+
var master_fd: c_int = -1;
|
|
105
|
+
const pid = c.forkpty(&master_fd, null, null, &winsize);
|
|
106
|
+
if (pid < 0) return -1;
|
|
107
|
+
|
|
108
|
+
if (pid == 0) {
|
|
109
|
+
// Child: adopt cwd/env then exec; _exit on any failure so a broken child never runs JS.
|
|
110
|
+
if (cwd != null and cwd[0] != 0) {
|
|
111
|
+
if (c.chdir(cwd) != 0) c._exit(127);
|
|
112
|
+
}
|
|
113
|
+
if (have_env) environ = &envp;
|
|
114
|
+
_ = c.execvp(argv[0], &argv);
|
|
115
|
+
c._exit(127);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Parent: non-blocking master so reads can poll without stalling the loop.
|
|
119
|
+
const flags = c.fcntl(master_fd, c.F_GETFL, @as(c_int, 0));
|
|
120
|
+
_ = c.fcntl(master_fd, c.F_SETFL, flags | c.O_NONBLOCK);
|
|
121
|
+
|
|
122
|
+
sessions[@intCast(handle)] = .{
|
|
123
|
+
.in_use = true,
|
|
124
|
+
.master_fd = master_fd,
|
|
125
|
+
.pid = pid,
|
|
126
|
+
.reaped = false,
|
|
127
|
+
.exit_code = 0,
|
|
128
|
+
};
|
|
129
|
+
return handle;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export fn cueloop_pty_write(handle: c_int, data: [*c]const u8, len: c_int) c_int {
|
|
133
|
+
const session = sessionOf(handle) orelse return -1;
|
|
134
|
+
const written = c.write(session.master_fd, data, @intCast(len));
|
|
135
|
+
return @intCast(written);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Non-blocking read of child output. Returns bytes read (>0), 0 when nothing is
|
|
139
|
+
// available yet, -2 once the child has exited and its output is drained, or -1.
|
|
140
|
+
export fn cueloop_pty_read(handle: c_int, buf: [*c]u8, len: c_int) c_int {
|
|
141
|
+
const session = sessionOf(handle) orelse return -1;
|
|
142
|
+
const bytes_read = c.read(session.master_fd, buf, @intCast(len));
|
|
143
|
+
if (bytes_read > 0) return @intCast(bytes_read);
|
|
144
|
+
if (bytes_read == 0) {
|
|
145
|
+
reapSession(session, true);
|
|
146
|
+
return -2; // EOF: slave closed
|
|
147
|
+
}
|
|
148
|
+
const err = std.c._errno().*;
|
|
149
|
+
if (err == c.EAGAIN or err == c.EWOULDBLOCK) {
|
|
150
|
+
reapSession(session, false);
|
|
151
|
+
return if (session.reaped) -2 else 0; // exited with no more output vs still running
|
|
152
|
+
}
|
|
153
|
+
if (err == c.EIO) {
|
|
154
|
+
reapSession(session, true);
|
|
155
|
+
return -2; // Linux: slave gone
|
|
156
|
+
}
|
|
157
|
+
return -1;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export fn cueloop_pty_resize(handle: c_int, cols: c_int, rows: c_int) c_int {
|
|
161
|
+
const session = sessionOf(handle) orelse return -1;
|
|
162
|
+
var winsize = std.mem.zeroes(c.struct_winsize);
|
|
163
|
+
winsize.ws_col = @intCast(cols);
|
|
164
|
+
winsize.ws_row = @intCast(rows);
|
|
165
|
+
return c.ioctl(session.master_fd, c.TIOCSWINSZ, &winsize);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export fn cueloop_pty_kill(handle: c_int) c_int {
|
|
169
|
+
const session = sessionOf(handle) orelse return -1;
|
|
170
|
+
return c.kill(session.pid, c.SIGTERM);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export fn cueloop_pty_get_pid(handle: c_int) c_int {
|
|
174
|
+
const session = sessionOf(handle) orelse return -1;
|
|
175
|
+
return @intCast(session.pid);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export fn cueloop_pty_get_exit_code(handle: c_int) c_int {
|
|
179
|
+
const session = sessionOf(handle) orelse return -1;
|
|
180
|
+
reapSession(session, true);
|
|
181
|
+
return session.exit_code;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export fn cueloop_pty_close(handle: c_int) void {
|
|
185
|
+
const session = sessionOf(handle) orelse return;
|
|
186
|
+
// Close the master first (hangs up the child), then block to reap it: after the
|
|
187
|
+
// SIGTERM from kill() plus this SIGHUP a well-behaved child exits at once, so the
|
|
188
|
+
// wait returns promptly and never leaves a zombie.
|
|
189
|
+
if (session.master_fd >= 0) _ = c.close(session.master_fd);
|
|
190
|
+
reapSession(session, true);
|
|
191
|
+
session.in_use = false;
|
|
192
|
+
session.master_fd = -1;
|
|
193
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// Flat FFI wrapper over libghostty-vt. It hides the allocator vtable, the
|
|
2
|
+
// by-value GhosttyPoint that bun:ffi cannot pass, and packed-cell decoding.
|
|
3
|
+
const std = @import("std");
|
|
4
|
+
const ghostty = @cImport({
|
|
5
|
+
@cInclude("ghostty/vt/types.h");
|
|
6
|
+
@cInclude("ghostty/vt/allocator.h");
|
|
7
|
+
@cInclude("ghostty/vt/terminal.h");
|
|
8
|
+
@cInclude("ghostty/vt/point.h");
|
|
9
|
+
@cInclude("ghostty/vt/grid_ref.h");
|
|
10
|
+
@cInclude("ghostty/vt/screen.h");
|
|
11
|
+
@cInclude("ghostty/vt/style.h");
|
|
12
|
+
@cInclude("ghostty/vt/color.h");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const CvtCell = extern struct {
|
|
16
|
+
codepoint: u32,
|
|
17
|
+
fg_r: u8,
|
|
18
|
+
fg_g: u8,
|
|
19
|
+
fg_b: u8,
|
|
20
|
+
fg_kind: u8,
|
|
21
|
+
bg_r: u8,
|
|
22
|
+
bg_g: u8,
|
|
23
|
+
bg_b: u8,
|
|
24
|
+
bg_kind: u8,
|
|
25
|
+
width: u8,
|
|
26
|
+
flags: u8,
|
|
27
|
+
_pad: u16,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
comptime {
|
|
31
|
+
std.debug.assert(@sizeOf(CvtCell) == 16);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export fn cvt_new(cols: u16, rows: u16) ?*anyopaque {
|
|
35
|
+
var terminal: ghostty.GhosttyTerminal = null;
|
|
36
|
+
if (ghostty.ghostty_terminal_new(null, &terminal, cols, rows) != ghostty.GHOSTTY_SUCCESS) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return terminal;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export fn cvt_free(terminal: ?*anyopaque) void {
|
|
43
|
+
if (terminal) |handle| ghostty.ghostty_terminal_free(@ptrCast(handle));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export fn cvt_write(terminal: *anyopaque, data: [*]const u8, len: usize) void {
|
|
47
|
+
ghostty.ghostty_terminal_vt_write(@ptrCast(terminal), data, len);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export fn cvt_resize(terminal: *anyopaque, cols: u16, rows: u16) void {
|
|
51
|
+
// Cell pixel size does not affect the character grid.
|
|
52
|
+
_ = ghostty.ghostty_terminal_resize(@ptrCast(terminal), cols, rows, 1, 1);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Writes [col u16 LE, row u16 LE, visible u8] into the caller's eight bytes.
|
|
56
|
+
export fn cvt_cursor(terminal: *anyopaque, out: [*]u8) void {
|
|
57
|
+
var x: u16 = 0;
|
|
58
|
+
var y: u16 = 0;
|
|
59
|
+
var visible = false;
|
|
60
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_X, &x);
|
|
61
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_Y, &y);
|
|
62
|
+
_ = ghostty.ghostty_terminal_get(@ptrCast(terminal), ghostty.GHOSTTY_TERMINAL_DATA_CURSOR_VISIBLE, &visible);
|
|
63
|
+
out[0] = @truncate(x);
|
|
64
|
+
out[1] = @truncate(x >> 8);
|
|
65
|
+
out[2] = @truncate(y);
|
|
66
|
+
out[3] = @truncate(y >> 8);
|
|
67
|
+
out[4] = @intFromBool(visible);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fn decodeColor(color: *const ghostty.GhosttyStyleColor, red: *u8, green: *u8, blue: *u8, kind: *u8) void {
|
|
71
|
+
if (color.tag == ghostty.GHOSTTY_STYLE_COLOR_RGB) {
|
|
72
|
+
kind.* = 2;
|
|
73
|
+
red.* = color.value.rgb.r;
|
|
74
|
+
green.* = color.value.rgb.g;
|
|
75
|
+
blue.* = color.value.rgb.b;
|
|
76
|
+
} else if (color.tag == ghostty.GHOSTTY_STYLE_COLOR_PALETTE) {
|
|
77
|
+
kind.* = 1;
|
|
78
|
+
red.* = @intCast(color.value.palette);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export fn cvt_cell(terminal: *anyopaque, x: u16, y: u32, out: *CvtCell) c_int {
|
|
83
|
+
out.* = std.mem.zeroes(CvtCell);
|
|
84
|
+
|
|
85
|
+
var point: ghostty.GhosttyPoint = undefined;
|
|
86
|
+
point.tag = ghostty.GHOSTTY_POINT_TAG_VIEWPORT;
|
|
87
|
+
point.value.coordinate.x = x;
|
|
88
|
+
point.value.coordinate.y = y;
|
|
89
|
+
|
|
90
|
+
var reference: ghostty.GhosttyGridRef = undefined;
|
|
91
|
+
if (ghostty.ghostty_terminal_grid_ref(@ptrCast(terminal), point, &reference) != ghostty.GHOSTTY_SUCCESS) return -1;
|
|
92
|
+
|
|
93
|
+
var cell: ghostty.GhosttyCell = undefined;
|
|
94
|
+
if (ghostty.ghostty_grid_ref_cell(&reference, &cell) != ghostty.GHOSTTY_SUCCESS) return -1;
|
|
95
|
+
|
|
96
|
+
_ = ghostty.ghostty_cell_get(cell, ghostty.GHOSTTY_CELL_DATA_CODEPOINT, &out.codepoint);
|
|
97
|
+
var width: u32 = 0;
|
|
98
|
+
_ = ghostty.ghostty_cell_get(cell, ghostty.GHOSTTY_CELL_DATA_WIDE, &width);
|
|
99
|
+
out.width = @intCast(width);
|
|
100
|
+
|
|
101
|
+
var style: ghostty.GhosttyStyle = undefined;
|
|
102
|
+
ghostty.ghostty_style_default(&style);
|
|
103
|
+
if (ghostty.ghostty_grid_ref_style(&reference, &style) == ghostty.GHOSTTY_SUCCESS) {
|
|
104
|
+
decodeColor(&style.fg_color, &out.fg_r, &out.fg_g, &out.fg_b, &out.fg_kind);
|
|
105
|
+
decodeColor(&style.bg_color, &out.bg_r, &out.bg_g, &out.bg_b, &out.bg_kind);
|
|
106
|
+
if (style.bold) out.flags |= 1;
|
|
107
|
+
if (style.italic) out.flags |= 2;
|
|
108
|
+
if (style.underline != 0) out.flags |= 4;
|
|
109
|
+
if (style.inverse) out.flags |= 8;
|
|
110
|
+
if (style.faint) out.flags |= 16;
|
|
111
|
+
if (style.strikethrough) out.flags |= 32;
|
|
112
|
+
}
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
package/package.json
CHANGED
|
@@ -1,46 +1,54 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cueloop/client",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.101",
|
|
4
|
+
"description": "The cueloop review UI: projection renderer, keyboard grammar, review rail",
|
|
5
|
+
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/mmurakaru/cueloop/issues"
|
|
8
|
+
},
|
|
4
9
|
"license": "Apache-2.0",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/mmurakaru/cueloop.git",
|
|
13
|
+
"directory": "packages/client"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"native",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
5
20
|
"type": "module",
|
|
6
21
|
"exports": {
|
|
7
|
-
".": "./src/index.ts"
|
|
22
|
+
".": "./src/index.ts",
|
|
23
|
+
"./config": "./src/settings/config.ts",
|
|
24
|
+
"./slash-palette": "./src/keyboard/slash-palette.ts"
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
8
28
|
},
|
|
9
29
|
"dependencies": {
|
|
10
|
-
"@cueloop/daemon": "0.1.0-alpha.
|
|
11
|
-
"@cueloop/extension-api": "0.1.0-alpha.
|
|
12
|
-
"@cueloop/integration-obsidian": "0.1.0-alpha.
|
|
13
|
-
"@cueloop/schema": "0.1.0-alpha.
|
|
14
|
-
"@opentui/core": "^0.
|
|
15
|
-
"@opentui/
|
|
16
|
-
"@opentui/
|
|
17
|
-
"@
|
|
18
|
-
"
|
|
19
|
-
"
|
|
30
|
+
"@cueloop/daemon": "0.1.0-alpha.101",
|
|
31
|
+
"@cueloop/extension-api": "0.1.0-alpha.101",
|
|
32
|
+
"@cueloop/integration-obsidian": "0.1.0-alpha.101",
|
|
33
|
+
"@cueloop/schema": "0.1.0-alpha.101",
|
|
34
|
+
"@opentui/core": "^0.5.14",
|
|
35
|
+
"@opentui/keymap": "0.5.14",
|
|
36
|
+
"@opentui/react": "^0.5.14",
|
|
37
|
+
"@opentui/ssh": "0.5.14",
|
|
38
|
+
"@pierre/diffs": "^1.5.1",
|
|
39
|
+
"@tanstack/virtual-core": "^3.17.11",
|
|
40
|
+
"diff": "^9",
|
|
41
|
+
"puppeteer-core": "^25.12.0",
|
|
42
|
+
"react": "^19.3.0",
|
|
43
|
+
"valibot": "^1.5.0",
|
|
44
|
+
"zustand": "^5.0.15"
|
|
20
45
|
},
|
|
21
46
|
"devDependencies": {
|
|
22
|
-
"@types/react": "^19.
|
|
23
|
-
"ssh2": "^1.
|
|
24
|
-
"
|
|
47
|
+
"@types/react": "^19.3.0",
|
|
48
|
+
"@types/ssh2": "^1.15.6",
|
|
49
|
+
"ssh2": "^1.17.0"
|
|
25
50
|
},
|
|
26
|
-
"publishConfig": {
|
|
27
|
-
"access": "public"
|
|
28
|
-
},
|
|
29
|
-
"files": [
|
|
30
|
-
"src",
|
|
31
|
-
"README.md"
|
|
32
|
-
],
|
|
33
51
|
"engines": {
|
|
34
52
|
"bun": ">=1.3.0"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
"type": "git",
|
|
38
|
-
"url": "git+https://github.com/mmurakaru/cueloop.git",
|
|
39
|
-
"directory": "packages/client"
|
|
40
|
-
},
|
|
41
|
-
"homepage": "https://github.com/mmurakaru/cueloop#readme",
|
|
42
|
-
"bugs": {
|
|
43
|
-
"url": "https://github.com/mmurakaru/cueloop/issues"
|
|
44
|
-
},
|
|
45
|
-
"description": "The cueloop review UI: projection renderer, keyboard grammar, review rail"
|
|
46
|
-
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared annotation palette: how a marked stretch, the caret cell, and a
|
|
3
|
+
* discussion card's accent edge paint, in every surface that hosts inline
|
|
4
|
+
* comments (the plan thread view and the diff sheet). One source, so a mark
|
|
5
|
+
* looks the same wherever it is drawn.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createTextAttributes } from "@opentui/core";
|
|
9
|
+
import type { Theme } from "../appearance/theme";
|
|
10
|
+
|
|
11
|
+
export const UNDERLINE = createTextAttributes({ underline: true });
|
|
12
|
+
export const CUT = createTextAttributes({ strikethrough: true, dim: true });
|
|
13
|
+
export const BOLD = createTextAttributes({ bold: true });
|
|
14
|
+
export const ITALIC = createTextAttributes({ italic: true });
|
|
15
|
+
|
|
16
|
+
/** The marked-words treatment: a violet backdrop under an underline. */
|
|
17
|
+
const MARK_BACKDROP_DARK = "#463852";
|
|
18
|
+
const MARK_BACKDROP_LIGHT = "#e6ddf5";
|
|
19
|
+
/** The discussion card's accent left edge. */
|
|
20
|
+
const CARD_EDGE_DARK = "#ab7aca";
|
|
21
|
+
const CARD_EDGE_LIGHT = "#8b5fd6";
|
|
22
|
+
/** The caret cell: a visible cursor painted on the character under the caret. */
|
|
23
|
+
const CARET_CELL_DARK = "#565b68";
|
|
24
|
+
const CARET_CELL_LIGHT = "#b8bcc8";
|
|
25
|
+
|
|
26
|
+
export interface AnnotationPalette {
|
|
27
|
+
markBackdrop: string;
|
|
28
|
+
cardEdge: string;
|
|
29
|
+
caretCell: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Dark tokens carry light text; use that to pick the palette variant. */
|
|
33
|
+
export function annotationPaletteFor(tokens: Theme): AnnotationPalette {
|
|
34
|
+
const dark = Number.parseInt(tokens.text.slice(1, 3) || "e4", 16) > 128;
|
|
35
|
+
|
|
36
|
+
return dark
|
|
37
|
+
? { markBackdrop: MARK_BACKDROP_DARK, cardEdge: CARD_EDGE_DARK, caretCell: CARET_CELL_DARK }
|
|
38
|
+
: {
|
|
39
|
+
markBackdrop: MARK_BACKDROP_LIGHT,
|
|
40
|
+
cardEdge: CARD_EDGE_LIGHT,
|
|
41
|
+
caretCell: CARET_CELL_LIGHT,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Focus cue: mix a hex color toward white, keeping its hue readable. */
|
|
46
|
+
export function lighten(hex: string, amount = 0.25): string {
|
|
47
|
+
const channels = hex.match(/^#(..)(..)(..)$/);
|
|
48
|
+
|
|
49
|
+
if (!channels) return hex;
|
|
50
|
+
const lifted = channels
|
|
51
|
+
.slice(1)
|
|
52
|
+
.map((channel) => {
|
|
53
|
+
const value = Number.parseInt(channel, 16);
|
|
54
|
+
|
|
55
|
+
return Math.round(value + (255 - value) * amount)
|
|
56
|
+
.toString(16)
|
|
57
|
+
.padStart(2, "0");
|
|
58
|
+
})
|
|
59
|
+
.join("");
|
|
60
|
+
|
|
61
|
+
return `#${lifted}`;
|
|
62
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Annotation } from "@cueloop/schema";
|
|
3
|
+
import { DARK } from "../../appearance/theme";
|
|
4
|
+
import { annotationPaletteFor } from "../annotation-palette";
|
|
5
|
+
import { slashItemsFrom } from "../../keyboard/slash-palette";
|
|
6
|
+
import { DEFAULT_QUICK_ACTIONS } from "../../settings/config";
|
|
7
|
+
import type { Story, StoryMeta } from "../../stories/story";
|
|
8
|
+
import { CommentRow, ComposerPalette, DiscussionCard } from "./AnnotationCards";
|
|
9
|
+
|
|
10
|
+
export const meta: StoryMeta = { title: "Chrome/AnnotationCards" };
|
|
11
|
+
|
|
12
|
+
const palette = annotationPaletteFor(DARK);
|
|
13
|
+
|
|
14
|
+
function annotation(id: string, body: string, author?: string): Annotation {
|
|
15
|
+
const comment: Annotation = {
|
|
16
|
+
id,
|
|
17
|
+
kind: "comment",
|
|
18
|
+
anchor: { quote: "the daemon", prefix: "", suffix: "" },
|
|
19
|
+
body,
|
|
20
|
+
createdAt: "2026-09-07T10:00:00.000Z",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (author !== undefined) comment.author = author;
|
|
24
|
+
|
|
25
|
+
return comment;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// A two-voice discussion: the owner's comment then a collaborator's reply, each
|
|
29
|
+
// segment wearing its own accent edge.
|
|
30
|
+
export const TwoVoices: Story = {
|
|
31
|
+
render: () => (
|
|
32
|
+
<DiscussionCard
|
|
33
|
+
tokens={DARK}
|
|
34
|
+
segments={[
|
|
35
|
+
{
|
|
36
|
+
color: palette.cardEdge,
|
|
37
|
+
node: <CommentRow annotation={annotation("a", "Which daemon?")} tokens={DARK} />,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
color: DARK.text,
|
|
41
|
+
node: (
|
|
42
|
+
<CommentRow
|
|
43
|
+
annotation={annotation("b", "The one the CLI spawns.", "fp:collab")}
|
|
44
|
+
tokens={DARK}
|
|
45
|
+
/>
|
|
46
|
+
),
|
|
47
|
+
},
|
|
48
|
+
]}
|
|
49
|
+
/>
|
|
50
|
+
),
|
|
51
|
+
expectedColors: [palette.cardEdge, DARK.text],
|
|
52
|
+
size: { width: 60, height: 8 },
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const FocusedCard: Story = {
|
|
56
|
+
render: () => (
|
|
57
|
+
<DiscussionCard
|
|
58
|
+
focused
|
|
59
|
+
tokens={DARK}
|
|
60
|
+
segments={[
|
|
61
|
+
{
|
|
62
|
+
color: palette.cardEdge,
|
|
63
|
+
node: (
|
|
64
|
+
<CommentRow annotation={annotation("a", "Needs an eviction story.")} tokens={DARK} />
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
]}
|
|
68
|
+
/>
|
|
69
|
+
),
|
|
70
|
+
expectedColors: [DARK.text],
|
|
71
|
+
size: { width: 60, height: 6 },
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// The palette a leading "/" opens under the composer, with the second item selected.
|
|
75
|
+
export const SlashPaletteOpen: Story = {
|
|
76
|
+
render: () => (
|
|
77
|
+
<ComposerPalette
|
|
78
|
+
slashActive
|
|
79
|
+
slashItems={slashItemsFrom(DEFAULT_QUICK_ACTIONS)}
|
|
80
|
+
slashIndex={1}
|
|
81
|
+
tokens={DARK}
|
|
82
|
+
/>
|
|
83
|
+
),
|
|
84
|
+
expectedColors: [DARK.accent, DARK.textDim],
|
|
85
|
+
size: { width: 90, height: 8 },
|
|
86
|
+
};
|