@tangle-network/ui 11.44.0 → 11.45.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/CHANGELOG.md +43 -0
- package/dist/_virtual/_rolldown/runtime.js +1 -2
- package/dist/auth/auth.d.ts +1 -3
- package/dist/auth/auth.js +1 -2
- package/dist/auth/login-layout.d.ts +1 -3
- package/dist/auth/login-layout.js +1 -2
- package/dist/chat/agent-timeline.d.ts +1 -3
- package/dist/chat/agent-timeline.js +1 -2
- package/dist/chat/chat-container.d.ts +1 -3
- package/dist/chat/chat-container.js +3 -4
- package/dist/chat/chat-message.d.ts +1 -3
- package/dist/chat/chat-message.js +1 -2
- package/dist/chat/message-author.d.ts +1 -3
- package/dist/chat/message-author.js +1 -2
- package/dist/chat/message-list.d.ts +1 -3
- package/dist/chat/message-list.js +1 -2
- package/dist/chat/synthetic-text.js +1 -2
- package/dist/chat/thinking-indicator.d.ts +1 -3
- package/dist/chat/thinking-indicator.js +1 -2
- package/dist/chat/transcript-text.d.ts +1 -3
- package/dist/chat/transcript-text.js +1 -2
- package/dist/chat/user-message.d.ts +1 -3
- package/dist/chat/user-message.js +1 -2
- package/dist/code-presentation.js +1 -2
- package/dist/editor/document-editor-pane.d.ts +1 -3
- package/dist/editor/document-editor-pane.js +3 -4
- package/dist/editor/editor-lazy.js +1 -2
- package/dist/editor/editor-loading.js +1 -2
- package/dist/editor/editor-peers.js +1 -2
- package/dist/editor/editor-provider.d.ts +1 -3
- package/dist/editor/editor-provider.js +1 -2
- package/dist/editor/editor-toolbar.d.ts +1 -3
- package/dist/editor/editor-toolbar.js +1 -2
- package/dist/editor/markdown-conversion.js +1 -2
- package/dist/editor/markdown-document-editor.js +1 -2
- package/dist/editor/source-markdown-editor.js +1 -2
- package/dist/editor/source-markdown.js +1 -2
- package/dist/editor/tiptap-editor.d.ts +0 -2
- package/dist/editor/tiptap-editor.js +1 -2
- package/dist/editor/use-editor.d.ts +1 -3
- package/dist/editor/use-editor.js +1 -2
- package/dist/files/file-artifact-pane.d.ts +1 -3
- package/dist/files/file-artifact-pane.js +6 -6
- package/dist/files/file-card.d.ts +1 -3
- package/dist/files/file-card.js +1 -2
- package/dist/files/file-format.d.ts +1 -3
- package/dist/files/file-format.js +1 -2
- package/dist/files/file-list.d.ts +1 -3
- package/dist/files/file-list.js +1 -2
- package/dist/files/file-preview.d.ts +1 -3
- package/dist/files/file-preview.js +1 -2
- package/dist/files/file-tabs.d.ts +8 -4
- package/dist/files/file-tabs.js +79 -15
- package/dist/files/file-tree.d.ts +1 -3
- package/dist/files/file-tree.js +1 -2
- package/dist/files/pdf-loader.d.ts +1 -3
- package/dist/files/pdf-loader.js +1 -2
- package/dist/files/pdf-viewer.d.ts +1 -3
- package/dist/files/pdf-viewer.js +1 -2
- package/dist/files/rich-file-tree.d.ts +1 -3
- package/dist/files/rich-file-tree.js +1 -2
- package/dist/hooks/use-auth.d.ts +1 -3
- package/dist/hooks/use-auth.js +1 -2
- package/dist/hooks/use-auto-scroll.d.ts +1 -3
- package/dist/hooks/use-auto-scroll.js +1 -2
- package/dist/hooks/use-dropdown-menu.d.ts +1 -2
- package/dist/hooks/use-dropdown-menu.js +1 -2
- package/dist/hooks/use-live-time.d.ts +1 -3
- package/dist/hooks/use-live-time.js +1 -2
- package/dist/hooks/use-realtime-session.d.ts +1 -3
- package/dist/hooks/use-realtime-session.js +1 -2
- package/dist/hooks/use-run-collapse-state.d.ts +1 -3
- package/dist/hooks/use-run-collapse-state.js +1 -2
- package/dist/hooks/use-run-groups.d.ts +1 -3
- package/dist/hooks/use-run-groups.js +1 -2
- package/dist/hooks/use-sdk-session.d.ts +1 -3
- package/dist/hooks/use-sdk-session.js +1 -2
- package/dist/hooks/use-sse-stream.d.ts +1 -3
- package/dist/hooks/use-sse-stream.js +1 -2
- package/dist/hooks/use-tool-call-stream.d.ts +1 -3
- package/dist/hooks/use-tool-call-stream.js +1 -2
- package/dist/lib/code-presentation.d.ts +1 -3
- package/dist/lib/code-presentation.js +1 -2
- package/dist/lib/control-presentation.d.ts +0 -2
- package/dist/lib/control-presentation.js +1 -2
- package/dist/lib/focus.d.ts +1 -3
- package/dist/lib/focus.js +1 -2
- package/dist/lib/interaction.d.ts +1 -3
- package/dist/lib/interaction.js +1 -2
- package/dist/lib/motion.d.ts +1 -3
- package/dist/lib/motion.js +1 -2
- package/dist/lib/optional-peer.js +1 -2
- package/dist/lib/series.d.ts +1 -3
- package/dist/lib/series.js +1 -2
- package/dist/lib/surface-motion.js +1 -2
- package/dist/lib/utils.d.ts +1 -3
- package/dist/lib/utils.js +1 -2
- package/dist/markdown/api-schema.d.ts +1 -3
- package/dist/markdown/api-schema.js +1 -2
- package/dist/markdown/code-block.d.ts +0 -2
- package/dist/markdown/code-block.js +1 -2
- package/dist/markdown/code-meta.d.ts +1 -3
- package/dist/markdown/code-meta.js +1 -2
- package/dist/markdown/markdown-table.d.ts +1 -3
- package/dist/markdown/markdown-table.js +1 -2
- package/dist/markdown/markdown.d.ts +1 -3
- package/dist/markdown/markdown.js +1 -2
- package/dist/nav.d.ts +0 -2
- package/dist/nav.js +1 -2
- package/dist/openui/openui-artifact-renderer.d.ts +1 -2
- package/dist/openui/openui-artifact-renderer.js +1 -2
- package/dist/openui/schema-compiled.js +1 -2
- package/dist/openui/schema-data.d.ts +1 -3
- package/dist/openui/schema-data.js +1 -2
- package/dist/openui-schema.d.ts +0 -2
- package/dist/openui-schema.js +1 -2
- package/dist/presentation.d.ts +0 -2
- package/dist/presentation.js +1 -2
- package/dist/primitives/alert-dialog.d.ts +0 -2
- package/dist/primitives/alert-dialog.js +1 -2
- package/dist/primitives/alert.d.ts +1 -3
- package/dist/primitives/alert.js +1 -2
- package/dist/primitives/artifact-pane.d.ts +1 -3
- package/dist/primitives/artifact-pane.js +1 -2
- package/dist/primitives/avatar.d.ts +0 -2
- package/dist/primitives/avatar.js +1 -2
- package/dist/primitives/badge.d.ts +0 -2
- package/dist/primitives/badge.js +1 -2
- package/dist/primitives/breadcrumb.d.ts +0 -2
- package/dist/primitives/breadcrumb.js +1 -2
- package/dist/primitives/button.d.ts +0 -2
- package/dist/primitives/button.js +1 -2
- package/dist/primitives/calendar.d.ts +0 -2
- package/dist/primitives/calendar.js +1 -2
- package/dist/primitives/card.d.ts +0 -2
- package/dist/primitives/card.js +1 -2
- package/dist/primitives/checkbox.d.ts +0 -2
- package/dist/primitives/checkbox.js +1 -2
- package/dist/primitives/chip.d.ts +0 -2
- package/dist/primitives/chip.js +1 -2
- package/dist/primitives/code-block.d.ts +0 -2
- package/dist/primitives/code-block.js +1 -2
- package/dist/primitives/collapsible.d.ts +0 -2
- package/dist/primitives/collapsible.js +1 -2
- package/dist/primitives/compliance-badge.d.ts +0 -2
- package/dist/primitives/compliance-badge.js +3 -4
- package/dist/primitives/context-menu.d.ts +0 -2
- package/dist/primitives/context-menu.js +1 -2
- package/dist/primitives/date-picker.d.ts +0 -2
- package/dist/primitives/date-picker.js +1 -2
- package/dist/primitives/dialog.d.ts +0 -2
- package/dist/primitives/dialog.js +3 -4
- package/dist/primitives/drop-zone.d.ts +1 -3
- package/dist/primitives/drop-zone.js +1 -2
- package/dist/primitives/dropdown-menu.d.ts +0 -2
- package/dist/primitives/dropdown-menu.js +1 -2
- package/dist/primitives/empty-state.d.ts +0 -2
- package/dist/primitives/empty-state.js +1 -2
- package/dist/primitives/field.d.ts +1 -3
- package/dist/primitives/field.js +1 -2
- package/dist/primitives/heading.d.ts +0 -2
- package/dist/primitives/heading.js +3 -4
- package/dist/primitives/help-text.d.ts +0 -2
- package/dist/primitives/help-text.js +1 -2
- package/dist/primitives/hover-card.d.ts +0 -2
- package/dist/primitives/hover-card.js +1 -2
- package/dist/primitives/icon-tile.d.ts +0 -2
- package/dist/primitives/icon-tile.js +1 -2
- package/dist/primitives/info-tip.js +1 -2
- package/dist/primitives/input.d.ts +0 -2
- package/dist/primitives/input.js +1 -2
- package/dist/primitives/label.d.ts +0 -2
- package/dist/primitives/label.js +1 -2
- package/dist/primitives/metric-chart.d.ts +0 -2
- package/dist/primitives/metric-chart.js +1 -2
- package/dist/primitives/metric-strip.d.ts +0 -2
- package/dist/primitives/metric-strip.js +1 -2
- package/dist/primitives/page-header.d.ts +0 -2
- package/dist/primitives/page-header.js +1 -2
- package/dist/primitives/page-shell.d.ts +1 -3
- package/dist/primitives/page-shell.js +1 -2
- package/dist/primitives/pagination.d.ts +1 -2
- package/dist/primitives/pagination.js +1 -2
- package/dist/primitives/popover.d.ts +0 -2
- package/dist/primitives/popover.js +1 -2
- package/dist/primitives/progress.d.ts +0 -2
- package/dist/primitives/progress.js +1 -2
- package/dist/primitives/radio-group.d.ts +0 -2
- package/dist/primitives/radio-group.js +1 -2
- package/dist/primitives/segmented-control.d.ts +1 -3
- package/dist/primitives/segmented-control.js +4 -5
- package/dist/primitives/select.d.ts +0 -2
- package/dist/primitives/select.js +1 -2
- package/dist/primitives/separator.d.ts +0 -2
- package/dist/primitives/separator.js +1 -2
- package/dist/primitives/series-sparkline.d.ts +0 -2
- package/dist/primitives/series-sparkline.js +1 -2
- package/dist/primitives/settings.d.ts +1 -3
- package/dist/primitives/settings.js +1 -2
- package/dist/primitives/sheet.d.ts +0 -2
- package/dist/primitives/sheet.js +3 -4
- package/dist/primitives/sidebar-drop-zone.d.ts +1 -3
- package/dist/primitives/sidebar-drop-zone.js +2 -3
- package/dist/primitives/skeleton.d.ts +0 -2
- package/dist/primitives/skeleton.js +1 -2
- package/dist/primitives/slider.d.ts +0 -2
- package/dist/primitives/slider.js +1 -2
- package/dist/primitives/stat-card.d.ts +0 -2
- package/dist/primitives/stat-card.js +1 -2
- package/dist/primitives/status-pill.d.ts +0 -2
- package/dist/primitives/status-pill.js +1 -2
- package/dist/primitives/switch.d.ts +0 -2
- package/dist/primitives/switch.js +1 -2
- package/dist/primitives/table.d.ts +0 -2
- package/dist/primitives/table.js +1 -2
- package/dist/primitives/tabs.d.ts +0 -2
- package/dist/primitives/tabs.js +1 -2
- package/dist/primitives/tag.d.ts +0 -2
- package/dist/primitives/tag.js +1 -2
- package/dist/primitives/terminal-display.d.ts +0 -2
- package/dist/primitives/terminal-display.js +1 -2
- package/dist/primitives/text-shimmer.d.ts +1 -3
- package/dist/primitives/text-shimmer.js +1 -2
- package/dist/primitives/theme-toggle.d.ts +1 -2
- package/dist/primitives/theme-toggle.js +1 -2
- package/dist/primitives/time-select.d.ts +0 -2
- package/dist/primitives/time-select.js +1 -2
- package/dist/primitives/toast.d.ts +1 -2
- package/dist/primitives/toast.js +1 -2
- package/dist/primitives/tone.d.ts +1 -3
- package/dist/primitives/tone.js +1 -2
- package/dist/primitives/toolbar.d.ts +0 -2
- package/dist/primitives/toolbar.js +1 -2
- package/dist/primitives/tooltip.d.ts +0 -2
- package/dist/primitives/tooltip.js +1 -2
- package/dist/primitives/upload-progress.d.ts +1 -3
- package/dist/primitives/upload-progress.js +1 -2
- package/dist/primitives/wizard.d.ts +1 -3
- package/dist/primitives/wizard.js +1 -2
- package/dist/redaction/redacted-document.d.ts +1 -3
- package/dist/redaction/redacted-document.js +1 -2
- package/dist/redaction.js +1 -2
- package/dist/run/approval-card.d.ts +1 -3
- package/dist/run/approval-card.js +1 -2
- package/dist/run/expandable-content.js +1 -2
- package/dist/run/expanded-tool-detail.d.ts +1 -3
- package/dist/run/expanded-tool-detail.js +1 -2
- package/dist/run/inline-thinking-item.d.ts +1 -3
- package/dist/run/inline-thinking-item.js +1 -2
- package/dist/run/inline-tool-item.d.ts +1 -3
- package/dist/run/inline-tool-item.js +1 -2
- package/dist/run/run-group.d.ts +1 -3
- package/dist/run/run-group.js +1 -2
- package/dist/run/run-item-primitives.d.ts +1 -3
- package/dist/run/run-item-primitives.js +1 -2
- package/dist/run/run-phase-list.d.ts +1 -3
- package/dist/run/run-phase-list.js +1 -2
- package/dist/run/run-row-shell.d.ts +1 -3
- package/dist/run/run-row-shell.js +1 -2
- package/dist/run/task-list.d.ts +1 -3
- package/dist/run/task-list.js +1 -2
- package/dist/run/tool-call-feed.d.ts +1 -3
- package/dist/run/tool-call-feed.js +1 -2
- package/dist/run/tool-call-step.d.ts +1 -3
- package/dist/run/tool-call-step.js +1 -2
- package/dist/stores/active-sessions-store.d.ts +1 -3
- package/dist/stores/active-sessions-store.js +1 -2
- package/dist/stores/chat-store.d.ts +1 -3
- package/dist/stores/chat-store.js +1 -2
- package/dist/tool-previews/command-preview.d.ts +1 -3
- package/dist/tool-previews/command-preview.js +1 -2
- package/dist/tool-previews/diff-preview.d.ts +1 -3
- package/dist/tool-previews/diff-preview.js +1 -2
- package/dist/tool-previews/glob-results-preview.d.ts +1 -3
- package/dist/tool-previews/glob-results-preview.js +1 -2
- package/dist/tool-previews/grep-results-preview.d.ts +1 -3
- package/dist/tool-previews/grep-results-preview.js +1 -2
- package/dist/tool-previews/preview-primitives.js +1 -2
- package/dist/tool-previews/question-preview.d.ts +1 -3
- package/dist/tool-previews/question-preview.js +1 -2
- package/dist/tool-previews/web-search-preview.d.ts +1 -3
- package/dist/tool-previews/web-search-preview.js +1 -2
- package/dist/tool-previews/write-file-preview.d.ts +1 -3
- package/dist/tool-previews/write-file-preview.js +1 -2
- package/dist/types/branding.d.ts +1 -3
- package/dist/types/message.d.ts +1 -3
- package/dist/types/parts.d.ts +1 -3
- package/dist/types/run.d.ts +1 -3
- package/dist/types/tool-display.d.ts +1 -3
- package/dist/utils/copy-text.d.ts +1 -3
- package/dist/utils/copy-text.js +1 -2
- package/dist/utils/format.d.ts +1 -3
- package/dist/utils/format.js +1 -2
- package/dist/utils/time-ago.d.ts +1 -3
- package/dist/utils/time-ago.js +1 -2
- package/dist/utils/tool-display.d.ts +1 -3
- package/dist/utils/tool-display.js +1 -2
- package/package.json +38 -38
- package/src/files/file-artifact-pane.tsx +1 -0
- package/src/files/file-tabs.test.tsx +66 -0
- package/src/files/file-tabs.tsx +77 -17
- package/src/primitives/heading.tsx +2 -2
- package/src/primitives/sidebar-drop-zone.tsx +1 -1
- package/src/styles/presentation.css +3 -3
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ToolPart } from "./parts.js";
|
|
2
2
|
import { ReactNode } from "react";
|
|
3
|
-
//#region src/types/tool-display.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Variant-specific rendering instructions for tool output.
|
|
6
5
|
* Maps directly to specialised preview components.
|
|
@@ -27,5 +26,4 @@ export interface ToolDisplayMetadata {
|
|
|
27
26
|
displayVariant?: DisplayVariant;
|
|
28
27
|
commandSnippet?: string;
|
|
29
28
|
targetPath?: string;
|
|
30
|
-
}
|
|
31
|
-
//#endregion
|
|
29
|
+
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
//#region src/utils/copy-text.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Copy text to clipboard with a non-secure-context fallback.
|
|
4
3
|
* Returns true if a copy strategy likely succeeded.
|
|
5
4
|
*/
|
|
6
|
-
export declare function copyText(text: string): Promise<boolean>;
|
|
7
|
-
//#endregion
|
|
5
|
+
export declare function copyText(text: string): Promise<boolean>;
|
package/dist/utils/copy-text.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/copy-text.ts
|
|
2
2
|
/**
|
|
3
3
|
* Copy text to clipboard with a non-secure-context fallback.
|
|
4
4
|
* Returns true if a copy strategy likely succeeded.
|
|
@@ -23,5 +23,4 @@ async function copyText(text) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
//#endregion
|
|
27
26
|
export { copyText };
|
package/dist/utils/format.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/utils/format.d.ts
|
|
2
1
|
/** Format a duration in milliseconds to a human-readable string. */
|
|
3
2
|
export declare function formatDuration(ms: number): string;
|
|
4
3
|
/**
|
|
@@ -24,5 +23,4 @@ export declare function formatUptime(ms: number): string;
|
|
|
24
23
|
* round above; GB keeps two decimals below 10 so half-GB changes stay
|
|
25
24
|
* visible on memory dashboards, and drops to one decimal above.
|
|
26
25
|
*/
|
|
27
|
-
export declare function formatBytes(bytes: number): string;
|
|
28
|
-
//#endregion
|
|
26
|
+
export declare function formatBytes(bytes: number): string;
|
package/dist/utils/format.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/format.ts
|
|
2
2
|
/** Format a duration in milliseconds to a human-readable string. */
|
|
3
3
|
function formatDuration(ms) {
|
|
4
4
|
if (ms < 1e3) return "<1s";
|
|
@@ -58,5 +58,4 @@ function formatBytes(bytes) {
|
|
|
58
58
|
const gb = mb / 1024;
|
|
59
59
|
return `${gb < 10 ? gb.toFixed(2) : gb.toFixed(1)} GB`;
|
|
60
60
|
}
|
|
61
|
-
//#endregion
|
|
62
61
|
export { formatBytes, formatDuration, formatUptime, plainTextFromMarkdown, truncateText };
|
package/dist/utils/time-ago.d.ts
CHANGED
package/dist/utils/time-ago.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/time-ago.ts
|
|
2
2
|
function timeAgo(ts) {
|
|
3
3
|
const secs = Math.floor((Date.now() - ts) / 1e3);
|
|
4
4
|
if (secs < 5) return "just now";
|
|
@@ -7,5 +7,4 @@ function timeAgo(ts) {
|
|
|
7
7
|
if (mins < 60) return `${mins}m ago`;
|
|
8
8
|
return `${Math.floor(mins / 60)}h ago`;
|
|
9
9
|
}
|
|
10
|
-
//#endregion
|
|
11
10
|
export { timeAgo };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { ToolPart } from "../types/parts.js";
|
|
2
2
|
import { ToolDisplayMetadata } from "../types/tool-display.js";
|
|
3
3
|
import { ToolCategory } from "../types/run.js";
|
|
4
|
-
//#region src/utils/tool-display.d.ts
|
|
5
4
|
export declare const TOOL_CATEGORY_ICONS: Record<ToolCategory, string>;
|
|
6
5
|
export declare function getToolCategory(toolName: string): ToolCategory;
|
|
7
6
|
/**
|
|
@@ -12,5 +11,4 @@ export declare function getToolCategory(toolName: string): ToolCategory;
|
|
|
12
11
|
*/
|
|
13
12
|
export declare function getToolDisplayMetadata(part: ToolPart): ToolDisplayMetadata;
|
|
14
13
|
/** Extract error text from a tool part, if any. */
|
|
15
|
-
export declare function getToolErrorText(part: ToolPart, fallback?: string): string | undefined;
|
|
16
|
-
//#endregion
|
|
14
|
+
export declare function getToolErrorText(part: ToolPart, fallback?: string): string | undefined;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
// src/utils/tool-display.ts
|
|
2
2
|
const TOOL_NAME_PREFIX = "tool:";
|
|
3
3
|
function normalizeToolName(tool) {
|
|
4
4
|
const n = tool?.toLowerCase() ?? "";
|
|
@@ -154,5 +154,4 @@ function getToolErrorText(part, fallback) {
|
|
|
154
154
|
if (part.state.status !== "error") return void 0;
|
|
155
155
|
return part.state.error || fallback;
|
|
156
156
|
}
|
|
157
|
-
//#endregion
|
|
158
157
|
export { TOOL_CATEGORY_ICONS, getToolCategory, getToolDisplayMetadata, getToolErrorText };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/ui",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.45.0",
|
|
4
4
|
"description": "Generic React UI components for Tangle products — primitives, chat, run, files, editor, markdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -130,43 +130,43 @@
|
|
|
130
130
|
"dependencies": {
|
|
131
131
|
"@pierre/theme": "1.1.0",
|
|
132
132
|
"@pierre/trees": "1.0.0-beta.6",
|
|
133
|
-
"@radix-ui/react-alert-dialog": "^1.1
|
|
134
|
-
"@radix-ui/react-avatar": "^1.
|
|
135
|
-
"@radix-ui/react-checkbox": "^1.
|
|
136
|
-
"@radix-ui/react-collapsible": "^1.1
|
|
137
|
-
"@radix-ui/react-context-menu": "^2.
|
|
138
|
-
"@radix-ui/react-dialog": "^1.1
|
|
139
|
-
"@radix-ui/react-dropdown-menu": "^2.1
|
|
133
|
+
"@radix-ui/react-alert-dialog": "^1.2.1",
|
|
134
|
+
"@radix-ui/react-avatar": "^1.3.1",
|
|
135
|
+
"@radix-ui/react-checkbox": "^1.4.1",
|
|
136
|
+
"@radix-ui/react-collapsible": "^1.2.1",
|
|
137
|
+
"@radix-ui/react-context-menu": "^2.4.1",
|
|
138
|
+
"@radix-ui/react-dialog": "^1.3.1",
|
|
139
|
+
"@radix-ui/react-dropdown-menu": "^2.2.1",
|
|
140
140
|
"@radix-ui/react-hover-card": "^1.2.1",
|
|
141
|
-
"@radix-ui/react-label": "^2.1.
|
|
142
|
-
"@radix-ui/react-popover": "^1.1
|
|
143
|
-
"@radix-ui/react-progress": "^1.1
|
|
144
|
-
"@radix-ui/react-radio-group": "^1.
|
|
145
|
-
"@radix-ui/react-select": "^2.
|
|
146
|
-
"@radix-ui/react-separator": "^1.
|
|
147
|
-
"@radix-ui/react-slider": "^1.
|
|
148
|
-
"@radix-ui/react-slot": "^1.
|
|
149
|
-
"@radix-ui/react-switch": "^1.
|
|
150
|
-
"@radix-ui/react-tabs": "^1.1
|
|
151
|
-
"@radix-ui/react-tooltip": "^1.
|
|
141
|
+
"@radix-ui/react-label": "^2.1.17",
|
|
142
|
+
"@radix-ui/react-popover": "^1.3.1",
|
|
143
|
+
"@radix-ui/react-progress": "^1.2.1",
|
|
144
|
+
"@radix-ui/react-radio-group": "^1.5.1",
|
|
145
|
+
"@radix-ui/react-select": "^2.4.1",
|
|
146
|
+
"@radix-ui/react-separator": "^1.2.0",
|
|
147
|
+
"@radix-ui/react-slider": "^1.6.1",
|
|
148
|
+
"@radix-ui/react-slot": "^1.5.0",
|
|
149
|
+
"@radix-ui/react-switch": "^1.4.1",
|
|
150
|
+
"@radix-ui/react-tabs": "^1.2.1",
|
|
151
|
+
"@radix-ui/react-tooltip": "^1.4.1",
|
|
152
152
|
"class-variance-authority": "^0.7.1",
|
|
153
153
|
"clsx": "^2.1.1",
|
|
154
154
|
"github-slugger": "^2.0.0",
|
|
155
|
-
"lucide-react": "^1.
|
|
156
|
-
"marked": "^18.
|
|
157
|
-
"motion": "14.
|
|
155
|
+
"lucide-react": "^1.61.0",
|
|
156
|
+
"marked": "^18.1.1",
|
|
157
|
+
"motion": "14.6.0",
|
|
158
158
|
"react-markdown": "^10.1.0",
|
|
159
159
|
"rehype-sanitize": "^6.0.0",
|
|
160
160
|
"rehype-slug": "^6.0.0",
|
|
161
161
|
"remark-gfm": "^4.0.1",
|
|
162
|
-
"shiki": "^4.
|
|
162
|
+
"shiki": "^4.5.0",
|
|
163
163
|
"tailwind-merge": "^3.7.0",
|
|
164
164
|
"turndown": "^7.2.4"
|
|
165
165
|
},
|
|
166
166
|
"peerDependencies": {
|
|
167
167
|
"@hocuspocus/provider": ">=2.15.0 <5.0.0",
|
|
168
168
|
"@nanostores/react": ">=0.8.4 <3.0.0",
|
|
169
|
-
"@tangle-network/brand": "^1.
|
|
169
|
+
"@tangle-network/brand": "^1.29.0",
|
|
170
170
|
"@tiptap/core": ">=3.20.4 <4.0.0",
|
|
171
171
|
"@tiptap/extension-collaboration": ">=3.20.4 <4.0.0",
|
|
172
172
|
"@tiptap/extension-collaboration-caret": ">=3.7.0 <4.0.0",
|
|
@@ -215,24 +215,24 @@
|
|
|
215
215
|
}
|
|
216
216
|
},
|
|
217
217
|
"devDependencies": {
|
|
218
|
-
"@hocuspocus/provider": "^4.7.
|
|
218
|
+
"@hocuspocus/provider": "^4.7.1",
|
|
219
219
|
"@nanostores/react": "^2.0.1",
|
|
220
|
-
"@storybook/react": "^10.6.
|
|
221
|
-
"@tiptap/core": "^3.
|
|
222
|
-
"@tiptap/extension-collaboration": "^3.
|
|
223
|
-
"@tiptap/extension-collaboration-caret": "^3.
|
|
224
|
-
"@tiptap/react": "^3.
|
|
225
|
-
"@tiptap/starter-kit": "^3.
|
|
226
|
-
"@tiptap/y-tiptap": "^3.0.
|
|
220
|
+
"@storybook/react": "^10.6.3",
|
|
221
|
+
"@tiptap/core": "^3.32.0",
|
|
222
|
+
"@tiptap/extension-collaboration": "^3.32.0",
|
|
223
|
+
"@tiptap/extension-collaboration-caret": "^3.32.0",
|
|
224
|
+
"@tiptap/react": "^3.32.0",
|
|
225
|
+
"@tiptap/starter-kit": "^3.32.0",
|
|
226
|
+
"@tiptap/y-tiptap": "^3.0.10",
|
|
227
227
|
"@types/react": "^19.3.0",
|
|
228
228
|
"@types/turndown": "^5.0.6",
|
|
229
|
-
"ajv": "8.
|
|
230
|
-
"nanostores": "^1.5.
|
|
229
|
+
"ajv": "8.20.0",
|
|
230
|
+
"nanostores": "^1.5.5",
|
|
231
231
|
"pdfjs-dist": "^6.4.299",
|
|
232
|
-
"react": "^19.3.
|
|
233
|
-
"react-dom": "^19.3.
|
|
234
|
-
"react-router": "^8.
|
|
235
|
-
"tsdown": "
|
|
232
|
+
"react": "^19.3.1",
|
|
233
|
+
"react-dom": "^19.3.1",
|
|
234
|
+
"react-router": "^8.5.0",
|
|
235
|
+
"tsdown": "0.23.1",
|
|
236
236
|
"typescript": "^7.0.2",
|
|
237
237
|
"yjs": "^13.6.33"
|
|
238
238
|
},
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { fireEvent, render, screen, within } from "@testing-library/react";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { FileTabs, type FileTabData } from "./file-tabs";
|
|
5
|
+
|
|
6
|
+
const TABS: FileTabData[] = [
|
|
7
|
+
{ id: "a", name: "records-graph-20261026a.md", path: "pages/graphs/records-graph-20261026a.md" },
|
|
8
|
+
{ id: "b", name: "parity.json", path: "run/parity.json", dirty: true },
|
|
9
|
+
{ id: "c", name: "README.md", path: "README.md" },
|
|
10
|
+
];
|
|
11
|
+
|
|
12
|
+
function Editor() {
|
|
13
|
+
const [tabs, setTabs] = useState(TABS);
|
|
14
|
+
const [active, setActive] = useState("a");
|
|
15
|
+
return (
|
|
16
|
+
<FileTabs
|
|
17
|
+
tabs={tabs}
|
|
18
|
+
activeId={active}
|
|
19
|
+
onSelect={setActive}
|
|
20
|
+
onClose={(id) => {
|
|
21
|
+
const at = tabs.findIndex((tab) => tab.id === id);
|
|
22
|
+
const rest = tabs.filter((tab) => tab.id !== id);
|
|
23
|
+
setTabs(rest);
|
|
24
|
+
if (id === active && rest.length) setActive(rest[Math.min(at, rest.length - 1)]!.id);
|
|
25
|
+
}}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("FileTabs", () => {
|
|
31
|
+
it("is one tab list with a single tab stop, named by each full file name", () => {
|
|
32
|
+
render(<Editor />);
|
|
33
|
+
const list = screen.getByRole("tablist", { name: "Open files" });
|
|
34
|
+
const tabs = within(list).getAllByRole("tab");
|
|
35
|
+
expect(tabs.map((tab) => tab.getAttribute("aria-label"))).toEqual([
|
|
36
|
+
"records-graph-20261026a.md",
|
|
37
|
+
"parity.json, unsaved changes",
|
|
38
|
+
"README.md",
|
|
39
|
+
]);
|
|
40
|
+
expect(tabs.map((tab) => tab.tabIndex)).toEqual([0, -1, -1]);
|
|
41
|
+
expect(screen.getByRole("tab", { name: "records-graph-20261026a.md" })).toHaveAttribute("aria-selected", "true");
|
|
42
|
+
expect(tabs[0]!.textContent).toBe("records-graph-20261026a.md");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("moves the selection with arrow keys, Home and End, and closes the focused tab with Delete", () => {
|
|
46
|
+
render(<Editor />);
|
|
47
|
+
const first = screen.getByRole("tab", { name: "records-graph-20261026a.md" });
|
|
48
|
+
first.focus();
|
|
49
|
+
fireEvent.keyDown(first, { key: "ArrowRight" });
|
|
50
|
+
expect(screen.getByRole("tab", { name: "parity.json, unsaved changes" })).toHaveAttribute("aria-selected", "true");
|
|
51
|
+
expect(document.activeElement).toBe(screen.getByRole("tab", { name: "parity.json, unsaved changes" }));
|
|
52
|
+
fireEvent.keyDown(document.activeElement!, { key: "End" });
|
|
53
|
+
expect(screen.getByRole("tab", { name: "README.md" })).toHaveAttribute("aria-selected", "true");
|
|
54
|
+
fireEvent.keyDown(document.activeElement!, { key: "ArrowRight" });
|
|
55
|
+
expect(screen.getByRole("tab", { name: "records-graph-20261026a.md" })).toHaveAttribute("aria-selected", "true");
|
|
56
|
+
fireEvent.keyDown(document.activeElement!, { key: "Delete" });
|
|
57
|
+
expect(screen.queryByRole("tab", { name: "records-graph-20261026a.md" })).toBeNull();
|
|
58
|
+
expect(screen.getByRole("tab", { name: "parity.json, unsaved changes" })).toHaveAttribute("aria-selected", "true");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("closes a tab from its own close button", () => {
|
|
62
|
+
render(<Editor />);
|
|
63
|
+
fireEvent.click(screen.getByRole("button", { name: "Close README.md" }));
|
|
64
|
+
expect(screen.getAllByRole("tab")).toHaveLength(2);
|
|
65
|
+
});
|
|
66
|
+
});
|
package/src/files/file-tabs.tsx
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FileTabs
|
|
2
|
+
* FileTabs: one tab per open file. The strip sits on its host's chrome plane and
|
|
3
|
+
* draws its baseline as an inset shadow (a host whose border is the baseline
|
|
4
|
+
* passes `shadow-none`); the active tab paints the card plane over it, so it
|
|
5
|
+
* joins the content below. A long name is cut in the middle, keeping its end
|
|
6
|
+
* and extension. Arrow keys, Home and End move the selection; Delete closes.
|
|
3
7
|
*/
|
|
4
8
|
|
|
9
|
+
import { useEffect, useRef, type KeyboardEvent } from "react";
|
|
5
10
|
import { X, FileText, FileCode, FileSpreadsheet } from "lucide-react";
|
|
6
|
-
import {
|
|
11
|
+
import { focusRingInset } from "../lib/focus";
|
|
12
|
+
import { controlMotion } from "../lib/motion";
|
|
7
13
|
import { cn } from "../lib/utils";
|
|
8
14
|
|
|
9
15
|
export interface FileTabData {
|
|
@@ -18,6 +24,8 @@ export interface FileTabsProps {
|
|
|
18
24
|
activeId?: string;
|
|
19
25
|
onSelect: (id: string) => void;
|
|
20
26
|
onClose: (id: string) => void;
|
|
27
|
+
/** Accessible name of the tab list. Defaults to "Open files". */
|
|
28
|
+
label?: string;
|
|
21
29
|
className?: string;
|
|
22
30
|
}
|
|
23
31
|
|
|
@@ -28,47 +36,99 @@ function getTabIcon(name: string) {
|
|
|
28
36
|
return FileCode;
|
|
29
37
|
}
|
|
30
38
|
|
|
31
|
-
|
|
39
|
+
/** A long name keeps its extension and the characters before it after the ellipsis. */
|
|
40
|
+
function splitName(name: string): [string, string] {
|
|
41
|
+
if (name.length <= 18) return [name, ""];
|
|
42
|
+
const dot = name.lastIndexOf(".");
|
|
43
|
+
const keep = Math.min(Math.max((dot > 0 ? name.length - dot : 0) + 6, 8), 12);
|
|
44
|
+
return [name.slice(0, -keep), name.slice(-keep)];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function FileTabs({ tabs, activeId, onSelect, onClose, label = "Open files", className }: FileTabsProps) {
|
|
48
|
+
const stripRef = useRef<HTMLDivElement>(null);
|
|
49
|
+
|
|
50
|
+
// Reveal the active tab inside the strip only; scrollIntoView would also scroll the page.
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
const strip = stripRef.current;
|
|
53
|
+
const tab = strip?.querySelector<HTMLElement>('[aria-selected="true"]')?.parentElement;
|
|
54
|
+
if (strip && tab) strip.scrollLeft = Math.min(tab.offsetLeft, Math.max(strip.scrollLeft, tab.offsetLeft + tab.offsetWidth - strip.clientWidth));
|
|
55
|
+
}, [activeId, tabs.length]);
|
|
56
|
+
|
|
32
57
|
if (tabs.length === 0) return null;
|
|
33
58
|
|
|
59
|
+
const tabButtons = () => [...(stripRef.current?.querySelectorAll<HTMLButtonElement>('[role="tab"]') ?? [])];
|
|
60
|
+
const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
61
|
+
const at = tabButtons().indexOf(event.target as HTMLButtonElement);
|
|
62
|
+
const last = tabs.length - 1;
|
|
63
|
+
const next = ({ ArrowRight: at === last ? 0 : at + 1, ArrowLeft: at === 0 ? last : at - 1, Home: 0, End: last } as Record<string, number>)[event.key];
|
|
64
|
+
if (at < 0 || (next === undefined && event.key !== "Delete")) return;
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
if (next !== undefined) {
|
|
67
|
+
tabButtons()[next]?.focus();
|
|
68
|
+
onSelect(tabs[next]!.id);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
onClose(tabs[at]!.id);
|
|
72
|
+
// Once the host has re-rendered, keep focus on the tab it opened.
|
|
73
|
+
requestAnimationFrame(() => (tabButtons().find((tab) => tab.getAttribute("aria-selected") === "true") ?? tabButtons().at(-1))?.focus());
|
|
74
|
+
};
|
|
75
|
+
|
|
34
76
|
return (
|
|
35
|
-
<div
|
|
77
|
+
<div
|
|
78
|
+
ref={stripRef}
|
|
79
|
+
role="tablist"
|
|
80
|
+
aria-label={label}
|
|
81
|
+
onKeyDown={onKeyDown}
|
|
82
|
+
className={cn(
|
|
83
|
+
"relative flex h-10 min-w-0 items-end gap-1 overflow-x-auto px-2 shadow-[inset_0_-1px_0_hsl(var(--border))] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",
|
|
84
|
+
className,
|
|
85
|
+
)}
|
|
86
|
+
>
|
|
36
87
|
{tabs.map((tab) => {
|
|
37
88
|
const isActive = tab.id === activeId;
|
|
38
89
|
const Icon = getTabIcon(tab.name);
|
|
90
|
+
const [head, tail] = splitName(tab.name);
|
|
39
91
|
|
|
40
92
|
return (
|
|
41
93
|
<div
|
|
42
94
|
key={tab.id}
|
|
95
|
+
title={tab.path}
|
|
43
96
|
className={cn(
|
|
44
|
-
"group flex items-center
|
|
97
|
+
"group relative flex h-[calc(100%-0.375rem)] max-w-72 shrink-0 items-center rounded-t-lg border border-b-0 text-sm",
|
|
98
|
+
controlMotion,
|
|
45
99
|
isActive
|
|
46
|
-
? "bg-card text-foreground
|
|
47
|
-
: "text-muted-foreground hover:bg-
|
|
100
|
+
? "border-border bg-card text-foreground"
|
|
101
|
+
: "border-transparent text-muted-foreground hover:bg-[var(--ix-hover-bg)] hover:text-foreground",
|
|
48
102
|
)}
|
|
49
103
|
>
|
|
50
104
|
<button
|
|
51
105
|
type="button"
|
|
106
|
+
role="tab"
|
|
107
|
+
aria-selected={isActive}
|
|
108
|
+
aria-label={tab.dirty ? `${tab.name}, unsaved changes` : tab.name}
|
|
109
|
+
tabIndex={isActive || (!activeId && tab === tabs[0]) ? 0 : -1}
|
|
52
110
|
onClick={() => onSelect(tab.id)}
|
|
53
|
-
className={cn(
|
|
54
|
-
"flex min-w-0 items-center gap-1.5 px-3 py-1.5 text-xs transition-colors hover:text-foreground",
|
|
55
|
-
focusRingInset,
|
|
56
|
-
)}
|
|
111
|
+
className={cn("flex h-full min-w-0 items-center gap-1.5 rounded-t-lg pl-3 pr-1", focusRingInset)}
|
|
57
112
|
>
|
|
58
|
-
<Icon className="
|
|
59
|
-
<span className="
|
|
60
|
-
|
|
113
|
+
<Icon aria-hidden className="size-3.5 shrink-0 text-muted-foreground" />
|
|
114
|
+
<span className="flex min-w-0">
|
|
115
|
+
<span className="overflow-hidden text-ellipsis whitespace-pre">{head}</span>
|
|
116
|
+
{tail && <span className="shrink-0 whitespace-pre">{tail}</span>}
|
|
117
|
+
</span>
|
|
118
|
+
{tab.dirty && <span aria-hidden className="size-1.5 shrink-0 rounded-full bg-[var(--accent-text)]" />}
|
|
61
119
|
</button>
|
|
62
120
|
<button
|
|
63
121
|
type="button"
|
|
122
|
+
tabIndex={-1}
|
|
64
123
|
aria-label={`Close ${tab.name}`}
|
|
65
124
|
onClick={() => onClose(tab.id)}
|
|
66
125
|
className={cn(
|
|
67
|
-
"mr-1
|
|
68
|
-
|
|
126
|
+
"mr-1.5 grid size-5 shrink-0 place-items-center rounded-md text-muted-foreground hover:bg-[var(--ix-hover-bg)] hover:text-foreground",
|
|
127
|
+
controlMotion,
|
|
128
|
+
!isActive && "opacity-0 group-focus-within:opacity-100 group-hover:opacity-100",
|
|
69
129
|
)}
|
|
70
130
|
>
|
|
71
|
-
<X className="
|
|
131
|
+
<X aria-hidden className="size-3" />
|
|
72
132
|
</button>
|
|
73
133
|
</div>
|
|
74
134
|
);
|
|
@@ -20,9 +20,9 @@ const VARIANT_CLASS: Record<HeadingVariant, string> = {
|
|
|
20
20
|
section:
|
|
21
21
|
"font-display font-semibold text-foreground text-[length:var(--font-size-xl,1.25rem)] leading-[1.3] tracking-[var(--tracking-snug,-0.01em)]",
|
|
22
22
|
subsection:
|
|
23
|
-
"font-semibold text-foreground text-[length:var(--font-size-
|
|
23
|
+
"font-semibold text-foreground text-[length:var(--font-size-base,1rem)] leading-[1.4]",
|
|
24
24
|
eyebrow:
|
|
25
|
-
"font-semibold uppercase text-muted-foreground text-[length:var(--font-size-
|
|
25
|
+
"font-semibold uppercase text-muted-foreground text-[length:var(--font-size-xs,0.75rem)] leading-none tracking-[var(--tracking-wide,0.08em)]",
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
const DEFAULT_TAG: Record<HeadingVariant, React.ElementType> = {
|
|
@@ -113,7 +113,7 @@ export function SidebarDropZone({
|
|
|
113
113
|
dragOver
|
|
114
114
|
? "border-[var(--brand-cool,hsl(var(--ring)))] bg-[var(--accent-surface-soft)]"
|
|
115
115
|
: persistent
|
|
116
|
-
? "border-[var(--border-subtle,hsl(var(--border)))] bg-transparent hover:border-[var(--border-
|
|
116
|
+
? "border-[var(--border-subtle,hsl(var(--border)))] bg-transparent hover:border-[var(--border-strong)] hover:bg-[var(--bg-hover,hsl(var(--accent)))]"
|
|
117
117
|
: "",
|
|
118
118
|
disabled && "opacity-50 pointer-events-none",
|
|
119
119
|
className,
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
.tangle-heading[data-variant="hero"] { font-size: var(--font-size-hero); line-height: var(--line-height-display); }
|
|
5
5
|
.tangle-heading[data-variant="page"] { font-size: var(--font-size-3xl); }
|
|
6
6
|
.tangle-heading[data-variant="section"] { font-size: var(--font-size-xl); }
|
|
7
|
-
.tangle-heading[data-variant="subsection"] { font-size: var(--font-size-
|
|
8
|
-
.tangle-heading[data-variant="eyebrow"] { font-size: var(--font-size-
|
|
7
|
+
.tangle-heading[data-variant="subsection"] { font-size: var(--font-size-base); }
|
|
8
|
+
.tangle-heading[data-variant="eyebrow"] { font-size: var(--font-size-xs); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: hsl(var(--muted-foreground)); }
|
|
9
9
|
.tangle-page-header { min-width: 0; margin-bottom: 1.5rem; }
|
|
10
10
|
.tangle-page-header-main { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 1.5rem; }
|
|
11
11
|
.tangle-intro .tangle-page-header-main { display: block; }
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
.tangle-native-dialog { max-width: min(32rem,calc(100% - 2rem)); max-height: calc(100dvh - 2rem); overflow: auto; margin: auto; padding: 1.5rem; border: 1px solid hsl(var(--border)); border-radius: var(--radius-lg); background: hsl(var(--card)); color: hsl(var(--card-foreground)); }
|
|
69
69
|
.tangle-native-dialog::backdrop { background: var(--scrim); }
|
|
70
70
|
|
|
71
|
-
.tangle-api-meta { font-family: var(--font-mono); font-size: var(--font-size-
|
|
71
|
+
.tangle-api-meta { font-family: var(--font-mono); font-size: var(--font-size-xs); color: hsl(var(--muted-foreground)); overflow-wrap: anywhere; }
|
|
72
72
|
.tangle-api-schema { margin: 1rem 0; }
|
|
73
73
|
.tangle-api-schema > div { padding-block: 0.75rem; border-bottom: 1px solid hsl(var(--border)); overflow-wrap: anywhere; }
|
|
74
74
|
.tangle-api-schema dt { font-family: var(--font-mono); font-size: var(--font-size-label); }
|