@iloveagents/foundry-web-ui 0.3.1 → 0.4.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/README.md +13 -4
- package/dist/components/ag-ui-runtime-provider.d.ts +64 -0
- package/dist/components/ag-ui-runtime-provider.js +71 -0
- package/dist/components/app-brand.d.ts +6 -0
- package/dist/components/app-brand.js +13 -0
- package/dist/components/assistant-chat.d.ts +26 -0
- package/dist/components/assistant-chat.js +93 -0
- package/dist/components/chat-attachments.d.ts +25 -0
- package/dist/components/chat-attachments.js +20 -0
- package/dist/components/chat-bubble.d.ts +1 -0
- package/dist/components/chat-bubble.js +103 -0
- package/dist/components/chat-header.d.ts +2 -0
- package/dist/components/chat-header.js +16 -0
- package/dist/components/client-tool-executor.d.ts +12 -0
- package/dist/components/client-tool-executor.js +108 -0
- package/dist/components/collection-empty-state.d.ts +9 -0
- package/dist/components/collection-empty-state.js +6 -0
- package/dist/components/collection-filter-bar.d.ts +19 -0
- package/dist/components/collection-filter-bar.js +28 -0
- package/dist/components/collection-search-input.d.ts +6 -0
- package/dist/components/collection-search-input.js +8 -0
- package/dist/components/collection-skeleton.d.ts +6 -0
- package/dist/components/collection-skeleton.js +10 -0
- package/dist/components/collection-sort-menu.d.ts +10 -0
- package/dist/components/collection-sort-menu.js +9 -0
- package/dist/components/collection-surface.d.ts +8 -0
- package/dist/components/collection-surface.js +5 -0
- package/dist/components/collection-toolbar.d.ts +9 -0
- package/dist/components/collection-toolbar.js +5 -0
- package/dist/components/collection-view-toggle.d.ts +7 -0
- package/dist/components/collection-view-toggle.js +17 -0
- package/dist/components/composer-submit-bridge.d.ts +1 -0
- package/dist/components/composer-submit-bridge.js +15 -0
- package/dist/components/confirm-dialog.d.ts +11 -0
- package/dist/components/confirm-dialog.js +6 -0
- package/dist/components/confirmation-card.d.ts +14 -0
- package/dist/components/confirmation-card.js +12 -0
- package/dist/components/context-badges.d.ts +16 -0
- package/dist/components/context-badges.js +56 -0
- package/dist/components/context-bar.d.ts +41 -0
- package/dist/components/context-bar.js +123 -0
- package/dist/components/form-dialog.d.ts +12 -0
- package/dist/components/form-dialog.js +13 -0
- package/dist/components/global-selection-popover.d.ts +7 -0
- package/dist/components/global-selection-popover.js +167 -0
- package/dist/components/infinite-scroll-sentinel.d.ts +6 -0
- package/dist/components/infinite-scroll-sentinel.js +25 -0
- package/dist/components/json-viewer.d.ts +10 -0
- package/dist/components/json-viewer.js +80 -0
- package/dist/components/loading-indicator.d.ts +4 -0
- package/dist/components/loading-indicator.js +22 -0
- package/dist/components/markdown-text.d.ts +13 -0
- package/dist/components/markdown-text.js +221 -0
- package/dist/components/name-dialog.d.ts +16 -0
- package/dist/components/name-dialog.js +23 -0
- package/dist/components/selection-popover.d.ts +12 -0
- package/dist/components/selection-popover.js +111 -0
- package/dist/components/show-document-tool-ui.d.ts +1 -0
- package/dist/components/show-document-tool-ui.js +53 -0
- package/dist/components/sidebar.d.ts +4 -0
- package/dist/components/sidebar.js +904 -0
- package/dist/components/surface-card.d.ts +9 -0
- package/dist/components/surface-card.js +33 -0
- package/dist/components/theme-runtime-provider.d.ts +15 -0
- package/dist/components/theme-runtime-provider.js +48 -0
- package/dist/components/theme-toggle.d.ts +2 -0
- package/dist/components/theme-toggle.js +19 -0
- package/dist/components/tool-call-card.d.ts +27 -0
- package/dist/components/tool-call-card.js +13 -0
- package/dist/components/tool-fallback.d.ts +26 -0
- package/dist/components/tool-fallback.js +129 -0
- package/dist/components/tool-panel-layout.d.ts +10 -0
- package/dist/components/tool-panel-layout.js +12 -0
- package/dist/components/tool-panel.d.ts +1 -0
- package/dist/components/tool-panel.js +89 -0
- package/dist/components/tooltip-icon-button.d.ts +7 -0
- package/dist/components/tooltip-icon-button.js +9 -0
- package/dist/components/user-menu.d.ts +2 -0
- package/dist/components/user-menu.js +14 -0
- package/dist/index.d.ts +68 -0
- package/dist/index.js +83 -0
- package/dist/lib/ag-ui-adapter.d.ts +29 -0
- package/dist/lib/ag-ui-adapter.js +373 -0
- package/dist/lib/app-store.d.ts +115 -0
- package/dist/lib/app-store.js +199 -0
- package/dist/lib/attachment-adapter.d.ts +20 -0
- package/dist/lib/attachment-adapter.js +99 -0
- package/dist/lib/auth-provider.d.ts +16 -0
- package/dist/lib/auth-provider.js +88 -0
- package/dist/lib/chat-bubble-store.d.ts +30 -0
- package/dist/lib/chat-bubble-store.js +23 -0
- package/dist/lib/chat-lifecycle-store.d.ts +10 -0
- package/{src/lib/chat-lifecycle-store.ts → dist/lib/chat-lifecycle-store.js} +3 -13
- package/dist/lib/client-tools.d.ts +11 -0
- package/dist/lib/client-tools.js +274 -0
- package/dist/lib/composer-submit-store.d.ts +12 -0
- package/dist/lib/composer-submit-store.js +17 -0
- package/dist/lib/dev-store.d.ts +38 -0
- package/dist/lib/dev-store.js +32 -0
- package/dist/lib/nav-config.d.ts +159 -0
- package/dist/lib/nav-config.js +162 -0
- package/dist/lib/nav-dnd.d.ts +8 -0
- package/dist/lib/nav-dnd.js +9 -0
- package/dist/lib/nav-store.d.ts +45 -0
- package/dist/lib/nav-store.js +49 -0
- package/dist/lib/selection-context.d.ts +21 -0
- package/dist/lib/selection-context.js +34 -0
- package/dist/lib/sidebar-store.d.ts +23 -0
- package/dist/lib/sidebar-store.js +98 -0
- package/dist/lib/theme-runtime.d.ts +130 -0
- package/dist/lib/theme-runtime.js +479 -0
- package/dist/lib/theme-store.d.ts +15 -0
- package/dist/lib/theme-store.js +53 -0
- package/dist/lib/tool-panel-store.d.ts +48 -0
- package/dist/lib/tool-panel-store.js +69 -0
- package/dist/lib/use-new-conversation.d.ts +8 -0
- package/dist/lib/use-new-conversation.js +62 -0
- package/dist/lib/use-page-tools.d.ts +27 -0
- package/{src/lib/use-page-tools.ts → dist/lib/use-page-tools.js} +11 -13
- package/dist/styles.css +142 -0
- package/dist/ui/collapsible.d.ts +5 -0
- package/{src/ui/collapsible.tsx → dist/ui/collapsible.js} +0 -2
- package/dist/ui/dialog.d.ts +15 -0
- package/dist/ui/dialog.js +25 -0
- package/dist/ui/dropdown-menu.d.ts +7 -0
- package/dist/ui/dropdown-menu.js +19 -0
- package/dist/ui/popover.d.ts +7 -0
- package/dist/ui/popover.js +10 -0
- package/dist/ui/select.d.ts +4 -0
- package/dist/ui/select.js +9 -0
- package/dist/ui/tooltip.d.ts +7 -0
- package/dist/ui/tooltip.js +10 -0
- package/package.json +31 -12
- package/AGENTS.md +0 -59
- package/CHANGELOG.md +0 -211
- package/CLAUDE.md +0 -1
- package/src/__tests__/no-spaces-imports.test.ts +0 -59
- package/src/__tests__/open-core-guards.test.ts +0 -307
- package/src/__tests__/theme-runtime.test.ts +0 -81
- package/src/__tests__/tool-fallback.test.tsx +0 -109
- package/src/components/__tests__/context-bar.test.ts +0 -112
- package/src/components/ag-ui-runtime-provider.tsx +0 -170
- package/src/components/app-brand.tsx +0 -38
- package/src/components/assistant-chat.tsx +0 -461
- package/src/components/chat-attachments.tsx +0 -68
- package/src/components/chat-bubble.tsx +0 -373
- package/src/components/chat-header.tsx +0 -44
- package/src/components/client-tool-executor.tsx +0 -230
- package/src/components/collection-empty-state.tsx +0 -37
- package/src/components/collection-filter-bar.tsx +0 -168
- package/src/components/collection-search-input.tsx +0 -41
- package/src/components/collection-skeleton.tsx +0 -55
- package/src/components/collection-sort-menu.tsx +0 -62
- package/src/components/collection-surface.tsx +0 -46
- package/src/components/collection-toolbar.tsx +0 -33
- package/src/components/collection-view-toggle.tsx +0 -61
- package/src/components/composer-submit-bridge.tsx +0 -19
- package/src/components/confirm-dialog.tsx +0 -49
- package/src/components/confirmation-card.tsx +0 -32
- package/src/components/context-badges.tsx +0 -136
- package/src/components/context-bar.tsx +0 -238
- package/src/components/form-dialog.tsx +0 -56
- package/src/components/global-selection-popover.tsx +0 -266
- package/src/components/infinite-scroll-sentinel.tsx +0 -48
- package/src/components/json-viewer.tsx +0 -232
- package/src/components/loading-indicator.tsx +0 -40
- package/src/components/markdown-text-citations.test.tsx +0 -108
- package/src/components/markdown-text.tsx +0 -508
- package/src/components/name-dialog.tsx +0 -87
- package/src/components/selection-popover.tsx +0 -156
- package/src/components/show-document-tool-ui.tsx +0 -90
- package/src/components/sidebar.test.tsx +0 -611
- package/src/components/sidebar.tsx +0 -1689
- package/src/components/surface-card.tsx +0 -45
- package/src/components/theme-runtime-provider.tsx +0 -93
- package/src/components/theme-toggle.tsx +0 -27
- package/src/components/tool-call-card.tsx +0 -126
- package/src/components/tool-fallback.tsx +0 -169
- package/src/components/tool-panel-layout.tsx +0 -36
- package/src/components/tool-panel.tsx +0 -233
- package/src/components/tooltip-icon-button.tsx +0 -28
- package/src/components/user-menu.tsx +0 -60
- package/src/index.ts +0 -185
- package/src/lib/__tests__/ag-ui-adapter.test.ts +0 -279
- package/src/lib/__tests__/app-store.test.ts +0 -405
- package/src/lib/__tests__/attachment-adapter.test.ts +0 -48
- package/src/lib/__tests__/chat-bubble-store.test.ts +0 -67
- package/src/lib/__tests__/client-tools.test.ts +0 -124
- package/src/lib/__tests__/dev-store.test.ts +0 -78
- package/src/lib/__tests__/nav-config.test.ts +0 -135
- package/src/lib/__tests__/nav-dnd.test.ts +0 -24
- package/src/lib/__tests__/nav-store.test.ts +0 -59
- package/src/lib/__tests__/selection-context.test.ts +0 -114
- package/src/lib/__tests__/sidebar-store.test.ts +0 -144
- package/src/lib/__tests__/theme-store.test.ts +0 -83
- package/src/lib/__tests__/tool-panel-store.test.ts +0 -61
- package/src/lib/__tests__/use-page-context.test.ts +0 -58
- package/src/lib/ag-ui-adapter.ts +0 -426
- package/src/lib/app-store.ts +0 -361
- package/src/lib/attachment-adapter.ts +0 -92
- package/src/lib/auth-provider.tsx +0 -144
- package/src/lib/chat-bubble-store.ts +0 -46
- package/src/lib/client-tools.ts +0 -293
- package/src/lib/composer-submit-store.ts +0 -32
- package/src/lib/dev-store.ts +0 -69
- package/src/lib/nav-config.ts +0 -329
- package/src/lib/nav-dnd.ts +0 -14
- package/src/lib/nav-store.ts +0 -89
- package/src/lib/selection-context.ts +0 -65
- package/src/lib/sidebar-store.ts +0 -113
- package/src/lib/theme-runtime.ts +0 -660
- package/src/lib/theme-store.ts +0 -67
- package/src/lib/tool-panel-store.ts +0 -121
- package/src/lib/use-new-conversation.test.tsx +0 -168
- package/src/lib/use-new-conversation.ts +0 -64
- package/src/ui/dialog.tsx +0 -109
- package/src/ui/dropdown-menu.tsx +0 -75
- package/src/ui/popover.tsx +0 -34
- package/src/ui/select.tsx +0 -15
- package/src/ui/tooltip.tsx +0 -30
- package/src/vite-env.d.ts +0 -1
- package/tsconfig.json +0 -16
- package/vitest.config.ts +0 -8
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Configuration — types, default config, and lookup utility.
|
|
3
|
+
*
|
|
4
|
+
* This is the single source of truth for the sidebar and agent context projection.
|
|
5
|
+
* The sidebar and app-layout read from the nav store (nav-store.ts), which is
|
|
6
|
+
* initialized with DEFAULT_NAV_CONFIG. Dynamic items (e.g., from a database)
|
|
7
|
+
* can be merged at runtime via useNavStore.getState().addGroup().
|
|
8
|
+
*/
|
|
9
|
+
import { MessageSquare,
|
|
10
|
+
// FileEdit,
|
|
11
|
+
// FolderOpen,
|
|
12
|
+
// BarChart3,
|
|
13
|
+
// ClipboardList,
|
|
14
|
+
// FileText,
|
|
15
|
+
} from "lucide-react";
|
|
16
|
+
/** Static default config — the template ships with these pages. */
|
|
17
|
+
export const DEFAULT_NAV_CONFIG = [
|
|
18
|
+
{
|
|
19
|
+
label: "Pages",
|
|
20
|
+
hideLabel: true,
|
|
21
|
+
items: [
|
|
22
|
+
{
|
|
23
|
+
to: "/",
|
|
24
|
+
label: "Chat",
|
|
25
|
+
icon: MessageSquare,
|
|
26
|
+
description: "Chat with the AI assistant",
|
|
27
|
+
},
|
|
28
|
+
// {
|
|
29
|
+
// to: "/dashboard",
|
|
30
|
+
// label: "Dashboard",
|
|
31
|
+
// icon: BarChart3,
|
|
32
|
+
// description: "Overview and analytics",
|
|
33
|
+
// },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
// {
|
|
37
|
+
// label: "Demos",
|
|
38
|
+
// description: "Demo pages for the Acme Corp engagement",
|
|
39
|
+
// meta: {
|
|
40
|
+
// workspaceId: "ws-acme-2026",
|
|
41
|
+
// client: "Acme Corp",
|
|
42
|
+
// region: "eu-west",
|
|
43
|
+
// permissions: ["read", "write", "comment"],
|
|
44
|
+
// },
|
|
45
|
+
// contextInstructions:
|
|
46
|
+
// "This is the Acme Corp demo workspace. Always address the user professionally. " +
|
|
47
|
+
// "When referencing documents or drafts, mention they belong to the Acme Corp project.",
|
|
48
|
+
// items: [
|
|
49
|
+
// {
|
|
50
|
+
// to: "/workspace",
|
|
51
|
+
// label: "Form",
|
|
52
|
+
// icon: ClipboardList,
|
|
53
|
+
// description:
|
|
54
|
+
// "Draft form with fields: title, description, category " +
|
|
55
|
+
// "(Bug/Feature/Task/Improvement), priority (Low/Medium/High/Critical), " +
|
|
56
|
+
// "assignee. Use ui_update_content to fill fields.",
|
|
57
|
+
// meta: { type: "form", formId: "default" },
|
|
58
|
+
// contextInstructions:
|
|
59
|
+
// "Always use ui_update_content to fill all fields in a single call. " +
|
|
60
|
+
// "Ask for confirmation before clearing the form. " +
|
|
61
|
+
// "Suggest appropriate category and priority based on the description.",
|
|
62
|
+
// },
|
|
63
|
+
// {
|
|
64
|
+
// to: "/documents",
|
|
65
|
+
// label: "Documents",
|
|
66
|
+
// icon: FolderOpen,
|
|
67
|
+
// description: "Browse and manage documents",
|
|
68
|
+
// children: [
|
|
69
|
+
// {
|
|
70
|
+
// to: "/documents",
|
|
71
|
+
// label: "All Documents",
|
|
72
|
+
// icon: FolderOpen,
|
|
73
|
+
// description: "Browse all documents",
|
|
74
|
+
// },
|
|
75
|
+
// {
|
|
76
|
+
// to: "/documents",
|
|
77
|
+
// label: "Engineering",
|
|
78
|
+
// icon: FileText,
|
|
79
|
+
// description: "Engineering documents",
|
|
80
|
+
// meta: { category: "Engineering" },
|
|
81
|
+
// },
|
|
82
|
+
// {
|
|
83
|
+
// to: "/documents",
|
|
84
|
+
// label: "Planning",
|
|
85
|
+
// icon: FileEdit,
|
|
86
|
+
// description: "Planning documents",
|
|
87
|
+
// meta: { category: "Planning" },
|
|
88
|
+
// },
|
|
89
|
+
// ],
|
|
90
|
+
// },
|
|
91
|
+
// ],
|
|
92
|
+
// },
|
|
93
|
+
];
|
|
94
|
+
function findExactNavItem(items, path) {
|
|
95
|
+
for (const item of items) {
|
|
96
|
+
if (item.to === path) {
|
|
97
|
+
return item;
|
|
98
|
+
}
|
|
99
|
+
if (item.children) {
|
|
100
|
+
const nested = findExactNavItem(item.children, path);
|
|
101
|
+
if (nested)
|
|
102
|
+
return nested;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function findBestPrefixNavItem(items, path, currentBest = null) {
|
|
108
|
+
let best = currentBest;
|
|
109
|
+
for (const item of items) {
|
|
110
|
+
if (path.startsWith(item.to + "/") && item.to.length > (best?.length ?? 0)) {
|
|
111
|
+
best = { item, length: item.to.length };
|
|
112
|
+
}
|
|
113
|
+
if (item.children) {
|
|
114
|
+
best = findBestPrefixNavItem(item.children, path, best);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return best;
|
|
118
|
+
}
|
|
119
|
+
export function findNavItem(config, path) {
|
|
120
|
+
// Pass 1: exact match on items
|
|
121
|
+
for (const group of config) {
|
|
122
|
+
const base = {
|
|
123
|
+
group: group.label,
|
|
124
|
+
groupMeta: group.meta,
|
|
125
|
+
groupDescription: group.description,
|
|
126
|
+
groupInstructions: group.contextInstructions,
|
|
127
|
+
};
|
|
128
|
+
const match = findExactNavItem(group.items, path);
|
|
129
|
+
if (match) {
|
|
130
|
+
return { ...base, item: match };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Pass 2: group-level `to` match (e.g. /spaces matches Workspaces group)
|
|
134
|
+
for (const group of config) {
|
|
135
|
+
if (group.to && group.to === path) {
|
|
136
|
+
return {
|
|
137
|
+
group: group.label,
|
|
138
|
+
groupMeta: group.meta,
|
|
139
|
+
groupDescription: group.description,
|
|
140
|
+
groupInstructions: group.contextInstructions,
|
|
141
|
+
item: { to: group.to, label: group.label, icon: MessageSquare },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Pass 3: longest-prefix match (for dynamic/parameterized routes)
|
|
146
|
+
let best = null;
|
|
147
|
+
let bestLen = 0;
|
|
148
|
+
for (const group of config) {
|
|
149
|
+
const base = {
|
|
150
|
+
group: group.label,
|
|
151
|
+
groupMeta: group.meta,
|
|
152
|
+
groupDescription: group.description,
|
|
153
|
+
groupInstructions: group.contextInstructions,
|
|
154
|
+
};
|
|
155
|
+
const match = findBestPrefixNavItem(group.items, path);
|
|
156
|
+
if (match && match.length > bestLen) {
|
|
157
|
+
best = { ...base, item: match.item };
|
|
158
|
+
bestLen = match.length;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return best;
|
|
162
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function hasExternalFilesData(dataTransfer: {
|
|
2
|
+
types: ArrayLike<string>;
|
|
3
|
+
files?: {
|
|
4
|
+
length: number;
|
|
5
|
+
} | null;
|
|
6
|
+
}): boolean;
|
|
7
|
+
export declare function hasAnyDropTarget(isDropTarget: boolean, isFileDropTarget: boolean): boolean;
|
|
8
|
+
export declare function getContainerDropZoneLabel(hasExternalFiles: boolean): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export function hasExternalFilesData(dataTransfer) {
|
|
2
|
+
return Array.from(dataTransfer.types).includes("Files") || (dataTransfer.files?.length ?? 0) > 0;
|
|
3
|
+
}
|
|
4
|
+
export function hasAnyDropTarget(isDropTarget, isFileDropTarget) {
|
|
5
|
+
return isDropTarget || isFileDropTarget;
|
|
6
|
+
}
|
|
7
|
+
export function getContainerDropZoneLabel(hasExternalFiles) {
|
|
8
|
+
return hasExternalFiles ? "Upload to root" : "Move to root";
|
|
9
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Store — Zustand store for the live navigation config.
|
|
3
|
+
*
|
|
4
|
+
* Initialized with DEFAULT_NAV_CONFIG (static pages).
|
|
5
|
+
* Dynamic groups/items (e.g., contract workspaces from a database)
|
|
6
|
+
* can be merged at runtime via addGroup() or setConfig().
|
|
7
|
+
*
|
|
8
|
+
* The sidebar and app-layout both subscribe to this store,
|
|
9
|
+
* so changes propagate automatically.
|
|
10
|
+
*/
|
|
11
|
+
import { type NavGroup } from "./nav-config.js";
|
|
12
|
+
/** A toggle or action rendered at the bottom of the sidebar. */
|
|
13
|
+
export interface NavFooterAction {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
activeLabel?: string;
|
|
17
|
+
active: boolean;
|
|
18
|
+
onToggle: () => void;
|
|
19
|
+
}
|
|
20
|
+
interface NavStoreState {
|
|
21
|
+
config: NavGroup[];
|
|
22
|
+
footerActions: NavFooterAction[];
|
|
23
|
+
/**
|
|
24
|
+
* True while an integrator's nav sync is loading its first batch of
|
|
25
|
+
* groups. Optional/defaulted (starts ``false``) so existing consumers
|
|
26
|
+
* that never set it are unaffected. The sidebar reads it to render
|
|
27
|
+
* skeleton rows instead of an empty list during the cold fetch —
|
|
28
|
+
* avoids the "empty sidebar for N seconds" gap on a slow nav sync.
|
|
29
|
+
*/
|
|
30
|
+
navLoading: boolean;
|
|
31
|
+
/** Replace the entire config */
|
|
32
|
+
setConfig: (config: NavGroup[]) => void;
|
|
33
|
+
/** Toggle the cold-load skeleton state. */
|
|
34
|
+
setNavLoading: (loading: boolean) => void;
|
|
35
|
+
/** Add a group (replaces existing group with the same label) */
|
|
36
|
+
addGroup: (group: NavGroup) => void;
|
|
37
|
+
/** Remove a group by label */
|
|
38
|
+
removeGroup: (label: string) => void;
|
|
39
|
+
/** Register a footer toggle (replaces existing by id) */
|
|
40
|
+
setFooterAction: (action: NavFooterAction) => void;
|
|
41
|
+
/** Remove a footer action by id */
|
|
42
|
+
removeFooterAction: (id: string) => void;
|
|
43
|
+
}
|
|
44
|
+
export declare const useNavStore: import("zustand").UseBoundStore<import("zustand").StoreApi<NavStoreState>>;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Store — Zustand store for the live navigation config.
|
|
3
|
+
*
|
|
4
|
+
* Initialized with DEFAULT_NAV_CONFIG (static pages).
|
|
5
|
+
* Dynamic groups/items (e.g., contract workspaces from a database)
|
|
6
|
+
* can be merged at runtime via addGroup() or setConfig().
|
|
7
|
+
*
|
|
8
|
+
* The sidebar and app-layout both subscribe to this store,
|
|
9
|
+
* so changes propagate automatically.
|
|
10
|
+
*/
|
|
11
|
+
import { create } from "zustand";
|
|
12
|
+
import { DEFAULT_NAV_CONFIG } from "./nav-config.js";
|
|
13
|
+
export const useNavStore = create((set, get) => ({
|
|
14
|
+
config: DEFAULT_NAV_CONFIG,
|
|
15
|
+
footerActions: [],
|
|
16
|
+
navLoading: false,
|
|
17
|
+
setConfig: (config) => set({ config }),
|
|
18
|
+
setNavLoading: (navLoading) => set({ navLoading }),
|
|
19
|
+
addGroup: (group) => {
|
|
20
|
+
const config = get().config;
|
|
21
|
+
const idx = config.findIndex((g) => g.label === group.label);
|
|
22
|
+
if (idx >= 0) {
|
|
23
|
+
const next = [...config];
|
|
24
|
+
next[idx] = group;
|
|
25
|
+
set({ config: next });
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
set({ config: [...config, group] });
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
removeGroup: (label) => {
|
|
32
|
+
set({ config: get().config.filter((g) => g.label !== label) });
|
|
33
|
+
},
|
|
34
|
+
setFooterAction: (action) => {
|
|
35
|
+
const actions = get().footerActions;
|
|
36
|
+
const idx = actions.findIndex((a) => a.id === action.id);
|
|
37
|
+
if (idx >= 0) {
|
|
38
|
+
const next = [...actions];
|
|
39
|
+
next[idx] = action;
|
|
40
|
+
set({ footerActions: next });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
set({ footerActions: [...actions, action] });
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
removeFooterAction: (id) => {
|
|
47
|
+
set({ footerActions: get().footerActions.filter((a) => a.id !== id) });
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ContextItem } from "./app-store.js";
|
|
2
|
+
export type SelectionContextItem = Omit<ContextItem, "id" | "createdAt">;
|
|
3
|
+
export interface SelectionContextInput {
|
|
4
|
+
text: string;
|
|
5
|
+
anchorNode: Node;
|
|
6
|
+
focusNode: Node | null;
|
|
7
|
+
range: Range;
|
|
8
|
+
sourcePage: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SelectionContextResult {
|
|
11
|
+
item: SelectionContextItem;
|
|
12
|
+
supportsModify?: boolean;
|
|
13
|
+
formatModifyPrompt?: (prompt: string) => string;
|
|
14
|
+
}
|
|
15
|
+
export interface SelectionContextResolver {
|
|
16
|
+
id: string;
|
|
17
|
+
priority?: number;
|
|
18
|
+
resolve: (input: SelectionContextInput) => SelectionContextResult | null;
|
|
19
|
+
}
|
|
20
|
+
export declare function registerSelectionContextResolver(resolver: SelectionContextResolver): () => void;
|
|
21
|
+
export declare function resolveSelectionContext(input: SelectionContextInput): SelectionContextResult;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const resolvers = new Map();
|
|
2
|
+
function defaultSelectionContext(input) {
|
|
3
|
+
return {
|
|
4
|
+
item: {
|
|
5
|
+
type: "selection",
|
|
6
|
+
label: input.text,
|
|
7
|
+
payload: { kind: "text", text: input.text },
|
|
8
|
+
sourcePage: input.sourcePage,
|
|
9
|
+
persistence: "ephemeral",
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function registerSelectionContextResolver(resolver) {
|
|
14
|
+
resolvers.set(resolver.id, resolver);
|
|
15
|
+
return () => {
|
|
16
|
+
if (resolvers.get(resolver.id) === resolver) {
|
|
17
|
+
resolvers.delete(resolver.id);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function resolveSelectionContext(input) {
|
|
22
|
+
const sortedResolvers = [...resolvers.values()].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
23
|
+
for (const resolver of sortedResolvers) {
|
|
24
|
+
try {
|
|
25
|
+
const resolved = resolver.resolve(input);
|
|
26
|
+
if (resolved)
|
|
27
|
+
return resolved;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// A module resolver must never break the generic selection flow.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return defaultSelectionContext(input);
|
|
34
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar State Management
|
|
3
|
+
*
|
|
4
|
+
* Zustand store for the collapsible left sidebar.
|
|
5
|
+
* Persists expanded/collapsed state to localStorage.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SIDEBAR_WIDTH = 272;
|
|
8
|
+
export declare const RAIL_WIDTH = 52;
|
|
9
|
+
export declare const SIDEBAR_MIN_WIDTH = 248;
|
|
10
|
+
export declare const SIDEBAR_MAX_WIDTH = 440;
|
|
11
|
+
interface SidebarState {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
isMobileOpen: boolean;
|
|
14
|
+
width: number;
|
|
15
|
+
toggle: () => void;
|
|
16
|
+
open: () => void;
|
|
17
|
+
close: () => void;
|
|
18
|
+
toggleMobile: () => void;
|
|
19
|
+
closeMobile: () => void;
|
|
20
|
+
setWidth: (width: number) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const useSidebarStore: import("zustand").UseBoundStore<import("zustand").StoreApi<SidebarState>>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar State Management
|
|
3
|
+
*
|
|
4
|
+
* Zustand store for the collapsible left sidebar.
|
|
5
|
+
* Persists expanded/collapsed state to localStorage.
|
|
6
|
+
*/
|
|
7
|
+
import { create } from "zustand";
|
|
8
|
+
export const SIDEBAR_WIDTH = 272;
|
|
9
|
+
export const RAIL_WIDTH = 52;
|
|
10
|
+
export const SIDEBAR_MIN_WIDTH = 248;
|
|
11
|
+
export const SIDEBAR_MAX_WIDTH = 440;
|
|
12
|
+
const MOBILE_BREAKPOINT = 768;
|
|
13
|
+
const STORAGE_KEY = "sidebar";
|
|
14
|
+
const WIDTH_STORAGE_KEY = "sidebar-width";
|
|
15
|
+
const WIDTH_VERSION_STORAGE_KEY = "sidebar-width-version";
|
|
16
|
+
const WIDTH_PERSIST_DELAY_MS = 120;
|
|
17
|
+
const SIDEBAR_WIDTH_VERSION = "2026-03-27-narrow";
|
|
18
|
+
let persistWidthTimer = null;
|
|
19
|
+
function clampWidth(width) {
|
|
20
|
+
return Math.min(SIDEBAR_MAX_WIDTH, Math.max(SIDEBAR_MIN_WIDTH, width));
|
|
21
|
+
}
|
|
22
|
+
function getStoredOpen() {
|
|
23
|
+
if (typeof window === "undefined")
|
|
24
|
+
return true;
|
|
25
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
26
|
+
if (stored === "true" || stored === "false")
|
|
27
|
+
return stored === "true";
|
|
28
|
+
// Default: collapsed rail — users expand when they want it
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function getStoredWidth() {
|
|
32
|
+
if (typeof window === "undefined")
|
|
33
|
+
return SIDEBAR_WIDTH;
|
|
34
|
+
const version = localStorage.getItem(WIDTH_VERSION_STORAGE_KEY);
|
|
35
|
+
if (version !== SIDEBAR_WIDTH_VERSION)
|
|
36
|
+
return SIDEBAR_WIDTH;
|
|
37
|
+
const raw = localStorage.getItem(WIDTH_STORAGE_KEY);
|
|
38
|
+
if (raw === null || raw.trim() === "")
|
|
39
|
+
return SIDEBAR_WIDTH;
|
|
40
|
+
const stored = Number(raw);
|
|
41
|
+
if (!Number.isFinite(stored))
|
|
42
|
+
return SIDEBAR_WIDTH;
|
|
43
|
+
return clampWidth(stored);
|
|
44
|
+
}
|
|
45
|
+
function persistWidth(width) {
|
|
46
|
+
if (typeof window === "undefined")
|
|
47
|
+
return;
|
|
48
|
+
if (persistWidthTimer)
|
|
49
|
+
window.clearTimeout(persistWidthTimer);
|
|
50
|
+
persistWidthTimer = window.setTimeout(() => {
|
|
51
|
+
localStorage.setItem(WIDTH_STORAGE_KEY, String(width));
|
|
52
|
+
localStorage.setItem(WIDTH_VERSION_STORAGE_KEY, SIDEBAR_WIDTH_VERSION);
|
|
53
|
+
persistWidthTimer = null;
|
|
54
|
+
}, WIDTH_PERSIST_DELAY_MS);
|
|
55
|
+
}
|
|
56
|
+
export const useSidebarStore = create((set, get) => ({
|
|
57
|
+
isOpen: getStoredOpen(),
|
|
58
|
+
isMobileOpen: false,
|
|
59
|
+
width: getStoredWidth(),
|
|
60
|
+
toggle: () => {
|
|
61
|
+
const next = !get().isOpen;
|
|
62
|
+
localStorage.setItem(STORAGE_KEY, String(next));
|
|
63
|
+
set({ isOpen: next });
|
|
64
|
+
},
|
|
65
|
+
open: () => {
|
|
66
|
+
localStorage.setItem(STORAGE_KEY, "true");
|
|
67
|
+
set({ isOpen: true });
|
|
68
|
+
},
|
|
69
|
+
close: () => {
|
|
70
|
+
localStorage.setItem(STORAGE_KEY, "false");
|
|
71
|
+
set({ isOpen: false });
|
|
72
|
+
},
|
|
73
|
+
toggleMobile: () => set({ isMobileOpen: !get().isMobileOpen }),
|
|
74
|
+
closeMobile: () => set({ isMobileOpen: false }),
|
|
75
|
+
setWidth: (width) => {
|
|
76
|
+
const next = clampWidth(width);
|
|
77
|
+
persistWidth(next);
|
|
78
|
+
set({ width: next });
|
|
79
|
+
},
|
|
80
|
+
}));
|
|
81
|
+
// Auto-close mobile drawer when resizing above breakpoint
|
|
82
|
+
if (typeof window !== "undefined") {
|
|
83
|
+
const version = localStorage.getItem(WIDTH_VERSION_STORAGE_KEY);
|
|
84
|
+
if (version !== SIDEBAR_WIDTH_VERSION) {
|
|
85
|
+
localStorage.setItem(WIDTH_STORAGE_KEY, String(SIDEBAR_WIDTH));
|
|
86
|
+
localStorage.setItem(WIDTH_VERSION_STORAGE_KEY, SIDEBAR_WIDTH_VERSION);
|
|
87
|
+
useSidebarStore.setState({ width: SIDEBAR_WIDTH });
|
|
88
|
+
}
|
|
89
|
+
let timeout;
|
|
90
|
+
window.addEventListener("resize", () => {
|
|
91
|
+
clearTimeout(timeout);
|
|
92
|
+
timeout = setTimeout(() => {
|
|
93
|
+
if (window.innerWidth >= MOBILE_BREAKPOINT) {
|
|
94
|
+
useSidebarStore.getState().closeMobile();
|
|
95
|
+
}
|
|
96
|
+
}, 100);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import type { ThemeMode } from "./theme-store.js";
|
|
3
|
+
export interface ThemeColorSlots {
|
|
4
|
+
background?: string;
|
|
5
|
+
foreground?: string;
|
|
6
|
+
card?: string;
|
|
7
|
+
cardForeground?: string;
|
|
8
|
+
popover?: string;
|
|
9
|
+
popoverForeground?: string;
|
|
10
|
+
primary?: string;
|
|
11
|
+
primaryForeground?: string;
|
|
12
|
+
secondary?: string;
|
|
13
|
+
secondaryForeground?: string;
|
|
14
|
+
muted?: string;
|
|
15
|
+
mutedForeground?: string;
|
|
16
|
+
accent?: string;
|
|
17
|
+
accentForeground?: string;
|
|
18
|
+
destructive?: string;
|
|
19
|
+
destructiveForeground?: string;
|
|
20
|
+
border?: string;
|
|
21
|
+
input?: string;
|
|
22
|
+
ring?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface ThemeSidebarSlots {
|
|
25
|
+
sidebar?: string;
|
|
26
|
+
sidebarForeground?: string;
|
|
27
|
+
sidebarPrimary?: string;
|
|
28
|
+
sidebarPrimaryForeground?: string;
|
|
29
|
+
sidebarAccent?: string;
|
|
30
|
+
sidebarAccentForeground?: string;
|
|
31
|
+
sidebarSelected?: string;
|
|
32
|
+
sidebarSelectedForeground?: string;
|
|
33
|
+
sidebarBorder?: string;
|
|
34
|
+
sidebarRing?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ThemeStatusSlots {
|
|
37
|
+
success?: string;
|
|
38
|
+
successForeground?: string;
|
|
39
|
+
warning?: string;
|
|
40
|
+
warningForeground?: string;
|
|
41
|
+
info?: string;
|
|
42
|
+
infoForeground?: string;
|
|
43
|
+
syntaxKey?: string;
|
|
44
|
+
syntaxString?: string;
|
|
45
|
+
syntaxNumber?: string;
|
|
46
|
+
syntaxBoolean?: string;
|
|
47
|
+
}
|
|
48
|
+
export interface ThemeChartSlots {
|
|
49
|
+
chart1?: string;
|
|
50
|
+
chart2?: string;
|
|
51
|
+
chart3?: string;
|
|
52
|
+
chart4?: string;
|
|
53
|
+
chart5?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface ThemeTypographySlots {
|
|
56
|
+
bodyFont?: string;
|
|
57
|
+
headingFont?: string;
|
|
58
|
+
monoFont?: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ThemeRadiusSlots {
|
|
61
|
+
radius?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ThemeAppSlots {
|
|
64
|
+
codeBlock?: string;
|
|
65
|
+
codeBlockForeground?: string;
|
|
66
|
+
workflowDiagramSurface?: string;
|
|
67
|
+
workflowDiagramLine?: string;
|
|
68
|
+
workflowDiagramText?: string;
|
|
69
|
+
workflowDiagramBorder?: string;
|
|
70
|
+
workflowDiagramCompletedStroke?: string;
|
|
71
|
+
workflowDiagramCompletedText?: string;
|
|
72
|
+
workflowDiagramActiveFill?: string;
|
|
73
|
+
workflowDiagramActiveStroke?: string;
|
|
74
|
+
workflowDiagramActiveText?: string;
|
|
75
|
+
workflowDiagramFailedFill?: string;
|
|
76
|
+
workflowDiagramFailedStroke?: string;
|
|
77
|
+
workflowDiagramFailedText?: string;
|
|
78
|
+
workflowDiagramSkippedFill?: string;
|
|
79
|
+
workflowDiagramSkippedStroke?: string;
|
|
80
|
+
workflowDiagramSkippedText?: string;
|
|
81
|
+
workflowDiagramGlow?: string;
|
|
82
|
+
workflowDiagramShell?: string;
|
|
83
|
+
}
|
|
84
|
+
export interface ThemeBranding {
|
|
85
|
+
appName?: string;
|
|
86
|
+
appTitle?: string;
|
|
87
|
+
logoUrl?: string;
|
|
88
|
+
darkLogoUrl?: string;
|
|
89
|
+
}
|
|
90
|
+
export interface ThemeDefinition {
|
|
91
|
+
light?: ThemeColorSlots;
|
|
92
|
+
dark?: ThemeColorSlots;
|
|
93
|
+
sidebar?: {
|
|
94
|
+
light?: ThemeSidebarSlots;
|
|
95
|
+
dark?: ThemeSidebarSlots;
|
|
96
|
+
};
|
|
97
|
+
status?: {
|
|
98
|
+
light?: ThemeStatusSlots;
|
|
99
|
+
dark?: ThemeStatusSlots;
|
|
100
|
+
};
|
|
101
|
+
charts?: {
|
|
102
|
+
light?: ThemeChartSlots;
|
|
103
|
+
dark?: ThemeChartSlots;
|
|
104
|
+
};
|
|
105
|
+
typography?: ThemeTypographySlots;
|
|
106
|
+
radius?: ThemeRadiusSlots;
|
|
107
|
+
app?: {
|
|
108
|
+
light?: ThemeAppSlots;
|
|
109
|
+
dark?: ThemeAppSlots;
|
|
110
|
+
};
|
|
111
|
+
branding?: ThemeBranding;
|
|
112
|
+
}
|
|
113
|
+
export interface ThemeLayer {
|
|
114
|
+
id?: string;
|
|
115
|
+
name?: string;
|
|
116
|
+
basePreset?: string | null;
|
|
117
|
+
definition?: ThemeDefinition | null;
|
|
118
|
+
}
|
|
119
|
+
export interface ResolvedThemeRuntime {
|
|
120
|
+
mode: "light" | "dark";
|
|
121
|
+
definition: ThemeDefinition;
|
|
122
|
+
branding: ThemeBranding;
|
|
123
|
+
variables: CSSProperties;
|
|
124
|
+
sources: ThemeLayer[];
|
|
125
|
+
}
|
|
126
|
+
export declare function mergeThemeDefinitions(...definitions: (ThemeDefinition | null | undefined)[]): ThemeDefinition;
|
|
127
|
+
export declare function getEffectiveThemeMode(mode: ThemeMode): "light" | "dark";
|
|
128
|
+
export declare const FOUNDRY_VIOLET_THEME: ThemeDefinition;
|
|
129
|
+
export declare function getThemePreset(name?: string | null): ThemeDefinition;
|
|
130
|
+
export declare function resolveThemeRuntime(layers: ThemeLayer[], mode: ThemeMode): ResolvedThemeRuntime;
|