@calliopelabs/ui 0.14.0 → 0.18.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.
Files changed (127) hide show
  1. package/README.md +235 -1
  2. package/dist/agent/AgentChat.d.ts +8 -1
  3. package/dist/agent/AgentChat.js +43 -2
  4. package/dist/agent/WorkflowRenderers.d.ts +61 -0
  5. package/dist/agent/WorkflowRenderers.js +102 -0
  6. package/dist/agent/controller.d.ts +19 -0
  7. package/dist/agent/controller.js +33 -0
  8. package/dist/agent/copilotkit/CopilotKitAgentProvider.js +2 -2
  9. package/dist/agent/index.d.ts +4 -2
  10. package/dist/agent/index.js +3 -1
  11. package/dist/agent/mock.d.ts +26 -4
  12. package/dist/agent/mock.js +68 -37
  13. package/dist/agent/renderers.d.ts +49 -0
  14. package/dist/agent/renderers.js +27 -0
  15. package/dist/appearance/brands.d.ts +17 -0
  16. package/dist/appearance/brands.js +14 -0
  17. package/dist/chat/MessageBubble.js +1 -1
  18. package/dist/chat/MessageList.d.ts +9 -2
  19. package/dist/chat/MessageList.js +11 -8
  20. package/dist/css/appearance.js +1 -1
  21. package/dist/css/calliope.css +5 -0
  22. package/dist/css/tokens.css +5 -0
  23. package/dist/index.d.ts +9 -1
  24. package/dist/index.js +8 -1
  25. package/dist/primitives/Badge.d.ts +14 -0
  26. package/dist/primitives/Badge.js +25 -0
  27. package/dist/primitives/ContextMenu.d.ts +18 -0
  28. package/dist/primitives/ContextMenu.js +34 -0
  29. package/dist/primitives/Menubar.d.ts +21 -0
  30. package/dist/primitives/Menubar.js +43 -0
  31. package/dist/primitives/ScrollArea.d.ts +16 -0
  32. package/dist/primitives/ScrollArea.js +11 -0
  33. package/dist/primitives/Toast.d.ts +37 -0
  34. package/dist/primitives/Toast.js +43 -0
  35. package/dist/primitives/Tooltip.d.ts +16 -0
  36. package/dist/primitives/Tooltip.js +26 -0
  37. package/dist/provider/messages.d.ts +217 -0
  38. package/dist/provider/messages.js +238 -0
  39. package/dist/shell/RailPanel.js +1 -1
  40. package/dist/theme/css-layer.js +2 -1
  41. package/dist/theme/tokens.generated.d.ts +3 -0
  42. package/dist/theme/tokens.generated.js +3 -0
  43. package/dist/workbench/ActivityBar.d.ts +37 -0
  44. package/dist/workbench/ActivityBar.js +57 -0
  45. package/dist/workbench/BottomPanel.d.ts +30 -0
  46. package/dist/workbench/BottomPanel.js +20 -0
  47. package/dist/workbench/CodeEditor.d.ts +48 -0
  48. package/dist/workbench/CodeEditor.js +153 -0
  49. package/dist/workbench/CommandPalette.d.ts +49 -0
  50. package/dist/workbench/CommandPalette.js +114 -0
  51. package/dist/workbench/DataChart.d.ts +45 -0
  52. package/dist/workbench/DataChart.js +146 -0
  53. package/dist/workbench/DataGrid.d.ts +89 -0
  54. package/dist/workbench/DataGrid.js +590 -0
  55. package/dist/workbench/DataProfile.d.ts +17 -0
  56. package/dist/workbench/DataProfile.js +51 -0
  57. package/dist/workbench/EditorTabs.d.ts +62 -0
  58. package/dist/workbench/EditorTabs.js +115 -0
  59. package/dist/workbench/FileBrowser.d.ts +62 -0
  60. package/dist/workbench/FileBrowser.js +117 -0
  61. package/dist/workbench/FileGrid.d.ts +13 -0
  62. package/dist/workbench/FileGrid.js +114 -0
  63. package/dist/workbench/FileIcon.d.ts +10 -0
  64. package/dist/workbench/FileIcon.js +24 -0
  65. package/dist/workbench/FileList.d.ts +35 -0
  66. package/dist/workbench/FileList.js +160 -0
  67. package/dist/workbench/FilePreview.d.ts +66 -0
  68. package/dist/workbench/FilePreview.js +108 -0
  69. package/dist/workbench/FileTree.d.ts +37 -0
  70. package/dist/workbench/FileTree.js +218 -0
  71. package/dist/workbench/GridCell.d.ts +19 -0
  72. package/dist/workbench/GridCell.js +50 -0
  73. package/dist/workbench/GridDetails.d.ts +31 -0
  74. package/dist/workbench/GridDetails.js +78 -0
  75. package/dist/workbench/MenuBar.d.ts +49 -0
  76. package/dist/workbench/MenuBar.js +25 -0
  77. package/dist/workbench/PaneGroup.d.ts +50 -0
  78. package/dist/workbench/PaneGroup.js +146 -0
  79. package/dist/workbench/PendingChangesBar.d.ts +20 -0
  80. package/dist/workbench/PendingChangesBar.js +19 -0
  81. package/dist/workbench/Shortcuts.d.ts +50 -0
  82. package/dist/workbench/Shortcuts.js +109 -0
  83. package/dist/workbench/SqlEditor.d.ts +21 -0
  84. package/dist/workbench/SqlEditor.js +79 -0
  85. package/dist/workbench/StatusBar.d.ts +28 -0
  86. package/dist/workbench/StatusBar.js +26 -0
  87. package/dist/workbench/TruncationBanner.d.ts +11 -0
  88. package/dist/workbench/TruncationBanner.js +12 -0
  89. package/dist/workbench/UploadDropzone.d.ts +52 -0
  90. package/dist/workbench/UploadDropzone.js +115 -0
  91. package/dist/workbench/WorkbenchShell.d.ts +36 -0
  92. package/dist/workbench/WorkbenchShell.js +20 -0
  93. package/dist/workbench/dnd.d.ts +17 -0
  94. package/dist/workbench/dnd.js +77 -0
  95. package/dist/workbench/download.d.ts +11 -0
  96. package/dist/workbench/download.js +28 -0
  97. package/dist/workbench/editor-theme.d.ts +42 -0
  98. package/dist/workbench/editor-theme.js +113 -0
  99. package/dist/workbench/grid-format.d.ts +59 -0
  100. package/dist/workbench/grid-format.js +169 -0
  101. package/dist/workbench/grid-model.d.ts +75 -0
  102. package/dist/workbench/grid-model.js +259 -0
  103. package/dist/workbench/grid-selection.d.ts +69 -0
  104. package/dist/workbench/grid-selection.js +183 -0
  105. package/dist/workbench/grid-stats.d.ts +58 -0
  106. package/dist/workbench/grid-stats.js +185 -0
  107. package/dist/workbench/index.d.ts +44 -0
  108. package/dist/workbench/index.js +47 -0
  109. package/dist/workbench/keys.d.ts +23 -0
  110. package/dist/workbench/keys.js +105 -0
  111. package/dist/workbench/model.d.ts +72 -0
  112. package/dist/workbench/model.js +214 -0
  113. package/dist/workbench/palette.d.ts +17 -0
  114. package/dist/workbench/palette.js +45 -0
  115. package/dist/workbench/pending.d.ts +92 -0
  116. package/dist/workbench/pending.js +207 -0
  117. package/dist/workbench/selection.d.ts +16 -0
  118. package/dist/workbench/selection.js +46 -0
  119. package/dist/workbench/sql-completion.d.ts +107 -0
  120. package/dist/workbench/sql-completion.js +475 -0
  121. package/dist/workbench/typeahead.d.ts +15 -0
  122. package/dist/workbench/typeahead.js +22 -0
  123. package/dist/workflow/WorkflowRuntimeControls.d.ts +21 -0
  124. package/dist/workflow/WorkflowRuntimeControls.js +54 -0
  125. package/dist/workflow/runtime-controls.d.ts +29 -0
  126. package/dist/workflow/runtime-controls.js +10 -0
  127. package/package.json +17 -2
@@ -144,6 +144,43 @@ function problemText({ code, values: v }) {
144
144
  }
145
145
  }
146
146
  export const defaultMessages = {
147
+ workflowRuntime: {
148
+ title: "Agent controls",
149
+ state: (state) => ({
150
+ running: "Agent running",
151
+ paused: "Agent paused",
152
+ "pause-requested": "Pause requested",
153
+ "resume-requested": "Resume requested",
154
+ "steering-queued": "Steering queued",
155
+ "steering-applied": "Steering applied",
156
+ refused: "Control refused",
157
+ "delivery-unknown": "Delivery unconfirmed",
158
+ unavailable: "Controls unavailable",
159
+ finished: "Agent finished",
160
+ })[state],
161
+ description: (state) => ({
162
+ running: "Control this agent while it works on the original goal.",
163
+ paused: "The agent confirmed its pause. Resume continues the same goal.",
164
+ "pause-requested": "Waiting for the agent to confirm its pause.",
165
+ "resume-requested": "Waiting for the agent to confirm it resumed.",
166
+ "steering-queued": "The agent has not yet confirmed this steering message.",
167
+ "steering-applied": "The agent applied the steering message to this goal.",
168
+ refused: "The command was refused. Check the current agent state before continuing.",
169
+ "delivery-unknown": "Check the original command's status before sending another command.",
170
+ unavailable: "The current agent controls could not be read.",
171
+ finished: "This agent is no longer accepting commands.",
172
+ })[state],
173
+ loading: "Loading agent controls…",
174
+ empty: "The original agent is not available.",
175
+ readOnly: "An operator can control this agent.",
176
+ pause: "Pause agent",
177
+ resume: "Resume agent",
178
+ refresh: "Check agent status",
179
+ steering: "Steering message",
180
+ steeringHint: "Up to 4,000 characters. Steering preserves the original goal and limits.",
181
+ invalidSteering: "Enter a nonempty message of up to 4,000 valid characters.",
182
+ send: "Send steering",
183
+ },
147
184
  workflowQuestion: {
148
185
  title: "Question for this run",
149
186
  worker: (worker) => `Worker: ${worker}`,
@@ -236,6 +273,202 @@ export const defaultMessages = {
236
273
  dialog: {
237
274
  close: "Close",
238
275
  },
276
+ workbench: {
277
+ files: "Files",
278
+ folders: "Folders",
279
+ root: "Home",
280
+ location: "Location",
281
+ filesIn: (folder) => `Files in ${folder}`,
282
+ view: "View",
283
+ listView: "List",
284
+ gridView: "Grid",
285
+ name: "Name",
286
+ size: "Size",
287
+ modified: "Modified",
288
+ noSize: "No size",
289
+ loading: "Loading files",
290
+ loadFailed: "Could not load this folder",
291
+ retry: "Retry",
292
+ retryFolder: "Could not load. Retry",
293
+ empty: "This folder is empty",
294
+ emptyDescription: "Drop files here to upload them.",
295
+ noFolders: "No folders",
296
+ itemCount: (count) => `${count.toLocaleString()} ${count === 1 ? "item" : "items"}`,
297
+ selected: (count) => `${count.toLocaleString()} selected`,
298
+ dropToUpload: "Drop files to upload",
299
+ dropHere: "Drop files here",
300
+ browse: "Choose files",
301
+ maxSize: (size) => `Up to ${size} each`,
302
+ uploads: "Uploads",
303
+ uploading: (count) => `Uploading ${count.toLocaleString()} ${count === 1 ? "file" : "files"}`,
304
+ uploadsDone: "Uploads finished",
305
+ uploadState: (state) => ({
306
+ queued: "Waiting",
307
+ uploading: "Uploading",
308
+ done: "Done",
309
+ error: "Failed",
310
+ canceled: "Cancelled",
311
+ })[state],
312
+ cancelUpload: (name) => `Cancel upload of ${name}`,
313
+ dismissUpload: (name) => `Clear ${name}`,
314
+ previewOf: (name) => `Preview of ${name}`,
315
+ previewLoading: "Loading preview",
316
+ previewFailed: "Could not preview this file",
317
+ noPreview: "No preview for this file",
318
+ closePreview: "Close preview",
319
+ contentsOf: (name) => `Contents of ${name}`,
320
+ showing: (shown, total) => total === null
321
+ ? `Showing the first ${shown.toLocaleString()}`
322
+ : `Showing ${shown.toLocaleString()} of ${total.toLocaleString()}`,
323
+ partial: "Showing the start of the file",
324
+ column: (index) => `Column ${index}`,
325
+ noRows: "No rows",
326
+ menuBar: "Menu bar",
327
+ activityBar: "Activity bar",
328
+ openEditors: "Open editors",
329
+ unsaved: "unsaved",
330
+ closeTab: (title) => `Close ${title}`,
331
+ allTabs: (count) => `All open tabs (${count.toLocaleString()})`,
332
+ resize: (pane) => `Resize ${pane}`,
333
+ maximizePanel: "Maximize panel",
334
+ restorePanel: "Restore panel",
335
+ closePanel: "Close panel",
336
+ statusBar: "Status bar",
337
+ commandPalette: "Command palette",
338
+ searchCommands: "Type a command or search",
339
+ noCommands: "No matching commands",
340
+ commandsLoading: "Loading",
341
+ recent: "Recent",
342
+ keyboardShortcuts: "Keyboard shortcuts",
343
+ keyboardShortcutsDescription: "Every keyboard shortcut this app responds to",
344
+ keyJoin: "+",
345
+ sideBar: "Side bar",
346
+ editor: "Editor",
347
+ panel: "Panel",
348
+ secondarySideBar: "Secondary side bar",
349
+ gridLabel: "Results",
350
+ gridRows: (shown, total) => shown === total
351
+ ? `${total.toLocaleString()} ${total === 1 ? "row" : "rows"}`
352
+ : `${shown.toLocaleString()} of ${total.toLocaleString()} rows`,
353
+ gridColumns: (count) => `${count.toLocaleString()} ${count === 1 ? "column" : "columns"}`,
354
+ gridFilter: "Filter rows",
355
+ gridClearFilter: "Clear filter",
356
+ gridHidden: (count) => `${count.toLocaleString()} hidden`,
357
+ gridSelected: (count) => `${count.toLocaleString()} selected`,
358
+ gridSelectAll: "Select all cells",
359
+ gridExport: "Export",
360
+ gridExportScope: (rows, columns) => `The current view: ${rows.toLocaleString()} ${rows === 1 ? "row" : "rows"}, ${columns.toLocaleString()} ${columns === 1 ? "column" : "columns"}`,
361
+ gridExportFormat: (format) => ({
362
+ csv: "CSV",
363
+ tsv: "TSV",
364
+ json: "JSON",
365
+ jsonl: "JSON Lines",
366
+ sql: "SQL INSERT",
367
+ markdown: "Markdown",
368
+ })[format],
369
+ gridSortBy: (column) => `Sort by ${column}`,
370
+ gridSortAscending: "Sort ascending",
371
+ gridSortDescending: "Sort descending",
372
+ gridClearSort: "Clear sort",
373
+ gridColumnMenu: (column) => `${column} column menu`,
374
+ gridFilterByValue: (value) => value === null
375
+ ? "Filter by value"
376
+ : `Filter by \u201c${value.length > 20 ? `${value.slice(0, 20)}\u2026` : value}\u201d`,
377
+ gridHideColumn: "Hide column",
378
+ gridShowAllColumns: (hidden) => `Show all columns (${hidden.toLocaleString()} hidden)`,
379
+ gridCopyColumnName: "Copy column name",
380
+ gridResizeColumn: (column) => `Resize ${column}`,
381
+ gridType: (type) => type,
382
+ gridPrimaryKey: "Primary key",
383
+ gridForeignKey: (references) => (references ? `Foreign key to ${references}` : "Foreign key"),
384
+ gridNullable: "nullable",
385
+ gridNotNull: "not null",
386
+ gridNull: "NULL",
387
+ gridEmptyText: "empty",
388
+ gridBinary: (bytes) => `binary, ${bytes.toLocaleString()} bytes`,
389
+ gridNoRows: "No rows",
390
+ gridNoMatches: "No rows match the filter",
391
+ gridLoading: "Loading rows",
392
+ gridLoadFailed: "Could not load the rows",
393
+ gridCopy: "Copy",
394
+ gridCopyWithHeaders: "Copy with headers",
395
+ gridCopyAs: "Copy as",
396
+ gridCopyFormat: (format) => ({
397
+ tsv: "TSV",
398
+ "tsv-headers": "TSV with headers",
399
+ csv: "CSV",
400
+ json: "JSON",
401
+ insert: "INSERT statements",
402
+ markdown: "Markdown",
403
+ })[format],
404
+ gridRowDetail: "Row detail",
405
+ gridCellDetail: "Expand cell",
406
+ gridClearSelection: "Clear selection",
407
+ gridStatistics: "Selection statistics",
408
+ gridStat: (stat) => ({
409
+ rows: "Rows",
410
+ nulls: "Nulls",
411
+ distinct: "Distinct",
412
+ min: "Min",
413
+ max: "Max",
414
+ avg: "Avg",
415
+ sum: "Sum",
416
+ })[stat],
417
+ gridNoNumeric: "No numeric values selected",
418
+ gridTruncated: (shown, total) => total === null
419
+ ? `Truncated at the ${shown.toLocaleString()}-row limit; more rows match.`
420
+ : `Showing ${shown.toLocaleString()} of ${total.toLocaleString()} rows, truncated at the ${shown.toLocaleString()}-row limit.`,
421
+ gridCharacters: (count) => `${count.toLocaleString()} ${count === 1 ? "character" : "characters"}`,
422
+ gridBytes: (count, truncated) => `${count.toLocaleString()} bytes${truncated ? ", preview truncated" : ""}`,
423
+ gridDownload: "Download",
424
+ gridCopied: "Copied",
425
+ gridCopyValue: "Copy",
426
+ gridRowOf: (row, total) => `Row ${row.toLocaleString()} of ${total.toLocaleString()}`,
427
+ gridPreviousRow: "Previous row",
428
+ gridNextRow: "Next row",
429
+ gridCopyRow: "Copy row",
430
+ gridAddRow: "Add row",
431
+ gridDeleteRows: (count) => count === 1 ? "Delete row" : `Delete ${count.toLocaleString()} rows`,
432
+ gridDuplicateRow: "Duplicate row",
433
+ gridRevertRow: "Revert row",
434
+ gridEditCell: (column) => `Edit ${column}`,
435
+ gridRowState: (state) => state,
436
+ gridPendingChanges: "Pending changes",
437
+ gridPendingSummary: (inserts, updates, deletes) => [
438
+ `${inserts.toLocaleString()} ${inserts === 1 ? "insert" : "inserts"}`,
439
+ `${updates.toLocaleString()} ${updates === 1 ? "update" : "updates"}`,
440
+ `${deletes.toLocaleString()} ${deletes === 1 ? "delete" : "deletes"}`,
441
+ ].join(", "),
442
+ gridCommit: "Commit",
443
+ gridCommitting: "Committing",
444
+ gridDiscard: "Discard",
445
+ gridCommitFailed: (message, row) => `${row === null ? "" : `Row ${row.toLocaleString()}: `}${message} Nothing was written.`,
446
+ gridNoPrimaryKey: "No primary key: rows can be added, not changed or deleted.",
447
+ profileLabel: "Column profile",
448
+ profileSummary: (rows, columns, nulls) => `Profile of ${rows.toLocaleString()} ${rows === 1 ? "row" : "rows"}, ${columns.toLocaleString()} ${columns === 1 ? "column" : "columns"}, ${nulls.toLocaleString()} ${nulls === 1 ? "null" : "nulls"}`,
449
+ profileKind: (kind) => kind,
450
+ profileComplete: (share) => `${share.toLocaleString(undefined, { style: "percent", maximumFractionDigits: 1 })} complete`,
451
+ profileMean: "Mean",
452
+ profileMedian: "Median",
453
+ profileTopValues: "Top values",
454
+ profileNoData: "No data to profile",
455
+ profileCap: (cap, fetched) => `Profiling the first ${cap.toLocaleString()} of ${fetched.toLocaleString()} rows.`,
456
+ chartLabel: "Chart",
457
+ chartType: "Chart type",
458
+ chartKind: (kind) => ({ bar: "Bar", line: "Line", area: "Area", scatter: "Scatter", pie: "Pie" })[kind],
459
+ chartX: "X axis",
460
+ chartY: "Y axis",
461
+ chartPickY: "Choose at least one Y axis column",
462
+ chartNoData: "No data to chart",
463
+ chartLoading: "Loading the chart",
464
+ chartLoadFailed: "Could not load the chart library",
465
+ chartCap: (cap, fetched) => `Charting the first ${cap.toLocaleString()} of ${fetched.toLocaleString()} rows.`,
466
+ editorLoading: "Loading the editor",
467
+ },
468
+ toast: {
469
+ region: "Notifications ({hotkey})",
470
+ close: "Dismiss notification",
471
+ },
239
472
  confirm: {
240
473
  confirm: "Confirm",
241
474
  cancel: "Cancel",
@@ -851,6 +1084,11 @@ export const defaultMessages = {
851
1084
  emptyDescription: "Ask about what is on this page, or pick a starting point.",
852
1085
  waitingOnYou: "Answer the card above to continue.",
853
1086
  shortcut: (keys) => `Shortcut ${keys}`,
1087
+ interrupt: "The agent is waiting for you",
1088
+ decline: "Decline",
1089
+ back: "Back",
1090
+ sendReview: "Send review",
1091
+ sharedState: "Shared state",
854
1092
  },
855
1093
  };
856
1094
  export function mergeMessages(overrides) {
@@ -27,7 +27,7 @@ export function RailPanel({ label, title, actions, footer, children, collapsed,
27
27
  }
28
28
  /* A focusable separator is the WAI-ARIA window splitter, an interactive
29
29
  widget; jsx-a11y counts every separator as static. */
30
- /* eslint-disable jsx-a11y/no-noninteractive-element-interactions, jsx-a11y/no-noninteractive-tabindex */
30
+ /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
31
31
  function ResizeHandle({ label, width, minWidth, maxWidth, onWidthChange, }) {
32
32
  const drag = React.useRef(null);
33
33
  return (_jsx("div", { role: "separator", "aria-orientation": "vertical", "aria-label": label, "aria-valuenow": width, "aria-valuemin": minWidth, "aria-valuemax": maxWidth, tabIndex: 0, onKeyDown: (event) => {
@@ -1,5 +1,5 @@
1
1
  import { BOOT_SOURCE, bootConfig } from "../appearance/boot.js";
2
- import { astroliftBrand, chatStudioBrand, cykickBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "../appearance/brands.js";
2
+ import { astroliftBrand, chatStudioBrand, cykickBrand, deepDataAgentBrand, loadrBrand, workbenchBrand, zentinelleBrand, } from "../appearance/brands.js";
3
3
  import { CORNERS, defineProduct } from "../appearance/product.js";
4
4
  import { accentDeclarations, block, renderThemeCss } from "./css.js";
5
5
  /**
@@ -16,6 +16,7 @@ export const CSS_LAYER_PRODUCTS = [
16
16
  chatStudioBrand,
17
17
  loadrBrand,
18
18
  cykickBrand,
19
+ deepDataAgentBrand,
19
20
  ].map((config) => defineProduct(config));
20
21
  const TOKENS_HEADER = `/*
21
22
  * @calliopelabs/ui CSS layer: tokens.
@@ -349,5 +349,8 @@ export declare const TOKENS: {
349
349
  readonly onLight: "#ffffff";
350
350
  };
351
351
  };
352
+ readonly "deep-data-agent": {
353
+ readonly sidebar: "#1a150c";
354
+ };
352
355
  };
353
356
  };
@@ -352,6 +352,9 @@ export const TOKENS = {
352
352
  "light": "#0e7490",
353
353
  "onLight": "#ffffff"
354
354
  }
355
+ },
356
+ "deep-data-agent": {
357
+ "sidebar": "#1a150c"
355
358
  }
356
359
  }
357
360
  };
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ export interface ActivityItem {
3
+ id: string;
4
+ /** The word under the icon ("Explore"). */
5
+ label: string;
6
+ /** The full name, spoken and shown on hover ("Database explorer"). Defaults to `label`. */
7
+ title?: string;
8
+ icon: React.ReactNode;
9
+ /** A count or marker on the icon (running tasks). */
10
+ badge?: React.ReactNode;
11
+ /** Pressed state for an item that is a toggle (the AI panel) rather than a view. */
12
+ pressed?: boolean;
13
+ /** Run instead of selecting the item as the view. */
14
+ onSelect?: () => void;
15
+ }
16
+ export interface ActivityBarProps {
17
+ /** The views, top down. */
18
+ items: readonly ActivityItem[];
19
+ /** Items pinned to the bottom (tasks, settings). */
20
+ footer?: readonly ActivityItem[];
21
+ /** The view on screen, or null when the side panel is closed. */
22
+ active: string | null;
23
+ /**
24
+ * A view was chosen. Choosing the active one again passes null: the
25
+ * host closes the side panel, as an IDE does.
26
+ */
27
+ onActiveChange: (id: string | null) => void;
28
+ /** Show the word under each icon. Defaults to true. */
29
+ showLabels?: boolean;
30
+ className?: string;
31
+ }
32
+ /**
33
+ * The rail of views down the left of an IDE layout: a vertical toolbar with
34
+ * one tab stop (Up and Down move, Home and End jump), each item a toggle
35
+ * button that says whether its view is open.
36
+ */
37
+ export declare function ActivityBar({ items, footer, active, onActiveChange, showLabels, className, }: ActivityBarProps): React.JSX.Element;
@@ -0,0 +1,57 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../primitives/Tooltip.js";
6
+ import { useUI } from "../provider/UIProvider.js";
7
+ /**
8
+ * The rail of views down the left of an IDE layout: a vertical toolbar with
9
+ * one tab stop (Up and Down move, Home and End jump), each item a toggle
10
+ * button that says whether its view is open.
11
+ */
12
+ export function ActivityBar({ items, footer = [], active, onActiveChange, showLabels = true, className, }) {
13
+ const { messages } = useUI();
14
+ const all = [...items, ...footer];
15
+ const [focusId, setFocusId] = React.useState(undefined);
16
+ const tabStop = all.some((i) => i.id === focusId)
17
+ ? focusId
18
+ : (all.find((i) => i.id === active)?.id ?? all[0]?.id);
19
+ const refs = React.useRef(new Map());
20
+ const onKeyDown = (event) => {
21
+ const index = all.findIndex((i) => i.id === tabStop);
22
+ let next = null;
23
+ if (event.key === "ArrowDown")
24
+ next = (index + 1) % all.length;
25
+ else if (event.key === "ArrowUp")
26
+ next = (index - 1 + all.length) % all.length;
27
+ else if (event.key === "Home")
28
+ next = 0;
29
+ else if (event.key === "End")
30
+ next = all.length - 1;
31
+ if (next === null)
32
+ return;
33
+ event.preventDefault();
34
+ const id = all[next]?.id;
35
+ if (id === undefined)
36
+ return;
37
+ setFocusId(id);
38
+ refs.current.get(id)?.focus();
39
+ };
40
+ const button = (item) => {
41
+ const pressed = item.pressed ?? item.id === active;
42
+ const title = item.title ?? item.label;
43
+ const count = typeof item.badge === "string" || typeof item.badge === "number" ? ` (${item.badge})` : "";
44
+ return (_jsxs(Tooltip, { delayDuration: 400, children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { ref: (el) => {
45
+ if (el)
46
+ refs.current.set(item.id, el);
47
+ else
48
+ refs.current.delete(item.id);
49
+ }, type: "button", tabIndex: item.id === tabStop ? 0 : -1, "aria-pressed": pressed, "aria-label": `${title}${count}`, onFocus: () => setFocusId(item.id), onClick: () => {
50
+ if (item.onSelect)
51
+ item.onSelect();
52
+ else
53
+ onActiveChange(item.id === active ? null : item.id);
54
+ }, className: cn("relative flex w-full flex-col items-center gap-0.5 py-2 text-sidebar-muted-foreground outline-hidden transition-colors hover:text-sidebar-foreground focus-visible:ring-2 focus-visible:ring-sidebar-ring focus-visible:ring-inset", pressed && "text-sidebar-primary hover:text-sidebar-primary"), children: [pressed && (_jsx("span", { "aria-hidden": true, className: "absolute top-1/2 left-0 h-8 w-0.5 -translate-y-1/2 rounded-full bg-sidebar-primary" })), _jsxs("span", { "aria-hidden": true, className: "relative flex [&_svg]:size-4.5", children: [item.icon, item.badge !== undefined && item.badge !== null && item.badge !== false && (_jsx("span", { className: "absolute -top-1.5 -right-2 min-w-4 rounded-full bg-sidebar-primary px-1 text-center text-2xs leading-4 text-sidebar-primary-foreground", children: item.badge }))] }), showLabels && (_jsx("span", { "aria-hidden": true, className: "text-2xs leading-none tracking-tight", children: item.label }))] }) }), _jsx(TooltipContent, { side: "right", children: title })] }, item.id));
55
+ };
56
+ return (_jsxs("div", { role: "toolbar", "aria-orientation": "vertical", "aria-label": messages.workbench.activityBar, onKeyDown: onKeyDown, className: cn("flex h-full w-14 shrink-0 flex-col border-r border-sidebar-border bg-sidebar text-sidebar-foreground", className), children: [_jsx("div", { className: "flex w-full flex-col", children: items.map(button) }), footer.length > 0 && (_jsx("div", { className: "mt-auto flex w-full flex-col pb-2", children: footer.map(button) }))] }));
57
+ }
@@ -0,0 +1,30 @@
1
+ import type * as React from "react";
2
+ export interface BottomPanelView {
3
+ id: string;
4
+ label: string;
5
+ icon?: React.ReactNode;
6
+ /** A count beside the label (history entries, running tasks). */
7
+ count?: number;
8
+ /** Something is running in it: the count takes the info tone. */
9
+ busy?: boolean;
10
+ content: React.ReactNode;
11
+ }
12
+ export interface BottomPanelProps {
13
+ views: readonly BottomPanelView[];
14
+ /** The view on screen. Controlled when given. */
15
+ value?: string;
16
+ defaultValue?: string;
17
+ onValueChange?: (id: string) => void;
18
+ /** The panel fills the editor area. Shows the maximize or restore button when `onMaximizedChange` is set. */
19
+ maximized?: boolean;
20
+ onMaximizedChange?: (maximized: boolean) => void;
21
+ /** Shows a close button. */
22
+ onClose?: () => void;
23
+ className?: string;
24
+ }
25
+ /**
26
+ * The panel under the editors (history, tasks, output, problems): views as
27
+ * ARIA tabs, with maximize, restore and close. The host decides whether it
28
+ * is open and how tall it is (in a `PaneGroup`); views stay host content.
29
+ */
30
+ export declare function BottomPanel({ views, value, defaultValue, onValueChange, maximized, onMaximizedChange, onClose, className, }: BottomPanelProps): React.JSX.Element;
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Maximize2Icon, Minimize2Icon, XIcon } from "lucide-react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "../primitives/Tabs.js";
6
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../primitives/Tooltip.js";
7
+ import { useUI } from "../provider/UIProvider.js";
8
+ function IconButton({ label, onClick, children, }) {
9
+ return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "aria-label": label, onClick: onClick, className: "rounded-lg p-1 text-muted-foreground outline-hidden transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:ring-2 focus-visible:ring-ring [&_svg]:size-3.5", children: children }) }), _jsx(TooltipContent, { children: label })] }));
10
+ }
11
+ /**
12
+ * The panel under the editors (history, tasks, output, problems): views as
13
+ * ARIA tabs, with maximize, restore and close. The host decides whether it
14
+ * is open and how tall it is (in a `PaneGroup`); views stay host content.
15
+ */
16
+ export function BottomPanel({ views, value, defaultValue, onValueChange, maximized = false, onMaximizedChange, onClose, className, }) {
17
+ const { messages } = useUI();
18
+ const m = messages.workbench;
19
+ return (_jsxs(Tabs, { value: value, defaultValue: defaultValue ?? views[0]?.id, onValueChange: onValueChange, className: cn("flex h-full min-h-0 flex-col gap-0 bg-background", className), children: [_jsxs("div", { className: "flex h-8 shrink-0 items-center border-b bg-surface-1 pr-1", children: [_jsx(TabsList, { className: "h-full flex-1 border-b-0", children: views.map((view) => (_jsxs(TabsTrigger, { value: view.id, className: "flex items-center gap-1.5 px-3 py-1 text-xs [&_svg]:size-3", children: [view.icon && (_jsx("span", { "aria-hidden": true, className: "flex", children: view.icon })), view.label, view.count !== undefined && view.count > 0 && (_jsxs(_Fragment, { children: [" ", _jsx("span", { className: cn("rounded-full px-1.5 text-2xs tabular-nums", view.busy ? "bg-info/15 text-info-fg" : "bg-muted text-muted-foreground"), children: view.count.toLocaleString() })] }))] }, view.id))) }), onMaximizedChange && (_jsx(IconButton, { label: maximized ? m.restorePanel : m.maximizePanel, onClick: () => onMaximizedChange(!maximized), children: maximized ? _jsx(Minimize2Icon, { "aria-hidden": true }) : _jsx(Maximize2Icon, { "aria-hidden": true }) })), onClose && (_jsx(IconButton, { label: m.closePanel, onClick: onClose, children: _jsx(XIcon, { "aria-hidden": true }) }))] }), views.map((view) => (_jsx(TabsContent, { value: view.id, className: "min-h-0 flex-1 overflow-hidden", children: view.content }, view.id)))] }));
20
+ }
@@ -0,0 +1,48 @@
1
+ import type * as Monaco from "monaco-editor";
2
+ import * as React from "react";
3
+ export type MonacoApi = typeof Monaco;
4
+ export type MonacoEditor = Monaco.editor.IStandaloneCodeEditor;
5
+ type Loader = () => Promise<MonacoApi | {
6
+ default: MonacoApi;
7
+ }>;
8
+ /**
9
+ * Tell the editors how to load Monaco, once, before any mounts:
10
+ * `configureMonaco({ load })`, where `load` imports `monaco-editor`. The import is
11
+ * the host's, so its bundler resolves Monaco, splits it into its own chunk
12
+ * and fetches it the first time an editor mounts; an app that never shows
13
+ * an editor never loads it, and one without `monaco-editor` installed still
14
+ * builds. The host also sets `self.MonacoEnvironment` for Monaco's workers
15
+ * (see the README). Without a loader, editors fall back to a plain text area.
16
+ */
17
+ export declare function configureMonaco(options: {
18
+ load: Loader;
19
+ }): void;
20
+ export interface CodeEditorProps {
21
+ value: string;
22
+ onChange?: (value: string) => void;
23
+ /** A Monaco language id: "sql", "json", "python", "typescript". Defaults to "plaintext". */
24
+ language?: string;
25
+ /** The editor's accessible name ("Query", "Pipeline spec"). */
26
+ label: string;
27
+ readOnly?: boolean;
28
+ /**
29
+ * Ctrl or Command with Enter: gets the selected text, or the whole buffer
30
+ * when nothing is selected.
31
+ */
32
+ onRun?: (text: string) => void;
33
+ /** Passed to Monaco as they are; the editor sets sensible defaults first. */
34
+ options?: Monaco.editor.IStandaloneEditorConstructionOptions;
35
+ /** The editor is ready. Return a cleanup to run when it unmounts. */
36
+ onMount?: (editor: MonacoEditor, monaco: MonacoApi) => void | (() => void);
37
+ /** Load Monaco for this editor only, instead of `configureMonaco`'s loader. Read on mount. */
38
+ loader?: Loader;
39
+ className?: string;
40
+ }
41
+ /**
42
+ * A code editor on Monaco, loaded the first time one mounts and themed from
43
+ * the library's tokens for the current ground (it follows a change of
44
+ * ground or mode). Monaco is an optional peer dependency: without it, or
45
+ * while it loads, a plain text area or a loading state stands in.
46
+ */
47
+ export declare function CodeEditor({ value, onChange, language, label, readOnly, onRun, options, onMount, loader, className, }: CodeEditorProps): React.JSX.Element;
48
+ export {};
@@ -0,0 +1,153 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/cn.js";
5
+ import { useUI } from "../provider/UIProvider.js";
6
+ import { LoadingState } from "../states/LoadingState.js";
7
+ import { editorTheme, readEditorPalette } from "./editor-theme.js";
8
+ let configured = null;
9
+ const loads = new WeakMap();
10
+ /**
11
+ * Tell the editors how to load Monaco, once, before any mounts:
12
+ * `configureMonaco({ load })`, where `load` imports `monaco-editor`. The import is
13
+ * the host's, so its bundler resolves Monaco, splits it into its own chunk
14
+ * and fetches it the first time an editor mounts; an app that never shows
15
+ * an editor never loads it, and one without `monaco-editor` installed still
16
+ * builds. The host also sets `self.MonacoEnvironment` for Monaco's workers
17
+ * (see the README). Without a loader, editors fall back to a plain text area.
18
+ */
19
+ export function configureMonaco(options) {
20
+ configured = options.load;
21
+ }
22
+ function loadMonaco(loader) {
23
+ let promise = loads.get(loader);
24
+ if (!promise) {
25
+ promise = loader().then((m) => ("default" in m && m.default ? m.default : m));
26
+ // A failed load may succeed later (a flaky network): do not cache it.
27
+ promise.catch(() => loads.delete(loader));
28
+ loads.set(loader, promise);
29
+ }
30
+ return promise;
31
+ }
32
+ const THEME = "calliope";
33
+ /** Define and apply the theme from the tokens under `element`. Monaco themes are page-wide. */
34
+ function applyTheme(monaco, element) {
35
+ const palette = readEditorPalette(element);
36
+ if (palette) {
37
+ monaco.editor.defineTheme(THEME, editorTheme(palette));
38
+ monaco.editor.setTheme(THEME);
39
+ }
40
+ else {
41
+ const dark = document.documentElement.getAttribute("data-mode") === "dark";
42
+ monaco.editor.setTheme(dark ? "vs-dark" : "vs");
43
+ }
44
+ }
45
+ /**
46
+ * A code editor on Monaco, loaded the first time one mounts and themed from
47
+ * the library's tokens for the current ground (it follows a change of
48
+ * ground or mode). Monaco is an optional peer dependency: without it, or
49
+ * while it loads, a plain text area or a loading state stands in.
50
+ */
51
+ export function CodeEditor({ value, onChange, language = "plaintext", label, readOnly = false, onRun, options, onMount, loader, className, }) {
52
+ const { messages } = useUI();
53
+ const hostRef = React.useRef(null);
54
+ const editorRef = React.useRef(null);
55
+ const monacoRef = React.useRef(null);
56
+ const latest = React.useRef({ value, onChange, onRun, onMount });
57
+ const [status, setStatus] = React.useState(() => (loader ?? configured) ? "loading" : "fallback");
58
+ React.useLayoutEffect(() => {
59
+ latest.current = { value, onChange, onRun, onMount };
60
+ });
61
+ // The loader of the first render: an inline arrow must not recreate the editor.
62
+ const loaderRef = React.useRef(loader ?? configured);
63
+ // Mount once. Value, language and read-only follow below.
64
+ React.useEffect(() => {
65
+ const load = loaderRef.current;
66
+ const host = hostRef.current;
67
+ if (!load || !host)
68
+ return;
69
+ let cancelled = false;
70
+ let cleanup;
71
+ let observer;
72
+ loadMonaco(load)
73
+ .then((monaco) => {
74
+ if (cancelled)
75
+ return;
76
+ monacoRef.current = monaco;
77
+ applyTheme(monaco, host);
78
+ const mono = getComputedStyle(host).getPropertyValue("--font-mono").trim();
79
+ const editor = monaco.editor.create(host, {
80
+ value: latest.current.value,
81
+ language,
82
+ readOnly,
83
+ ariaLabel: label,
84
+ automaticLayout: true,
85
+ minimap: { enabled: false },
86
+ scrollBeyondLastLine: false,
87
+ fontSize: 13,
88
+ fontFamily: mono || undefined,
89
+ tabSize: 2,
90
+ renderLineHighlight: "line",
91
+ fixedOverflowWidgets: true,
92
+ ...options,
93
+ });
94
+ editorRef.current = editor;
95
+ editor.onDidChangeModelContent(() => {
96
+ const next = editor.getValue();
97
+ if (next !== latest.current.value)
98
+ latest.current.onChange?.(next);
99
+ });
100
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => {
101
+ const selection = editor.getSelection();
102
+ const model = editor.getModel();
103
+ const picked = selection && model && !selection.isEmpty() ? model.getValueInRange(selection) : "";
104
+ latest.current.onRun?.(picked || editor.getValue());
105
+ });
106
+ // The theme follows the ground: re-read the tokens when <html> changes.
107
+ observer = new MutationObserver(() => applyTheme(monaco, host));
108
+ observer.observe(document.documentElement, { attributes: true });
109
+ const unmount = latest.current.onMount?.(editor, monaco);
110
+ cleanup = typeof unmount === "function" ? unmount : undefined;
111
+ setStatus("ready");
112
+ })
113
+ .catch(() => {
114
+ if (!cancelled)
115
+ setStatus("fallback");
116
+ });
117
+ return () => {
118
+ cancelled = true;
119
+ observer?.disconnect();
120
+ cleanup?.();
121
+ editorRef.current?.dispose();
122
+ editorRef.current = null;
123
+ };
124
+ // Recreating the editor on every option change would lose the undo stack
125
+ // and the cursor; those that can change are applied in place below.
126
+ // eslint-disable-next-line react-hooks/exhaustive-deps
127
+ }, []);
128
+ React.useEffect(() => {
129
+ const editor = editorRef.current;
130
+ const model = editor?.getModel();
131
+ if (!editor || !model || model.getValue() === value)
132
+ return;
133
+ // An edit, not setValue, so the person can undo past a host change.
134
+ editor.pushUndoStop();
135
+ model.pushEditOperations([], [{ range: model.getFullModelRange(), text: value }], () => null);
136
+ editor.pushUndoStop();
137
+ }, [value, status]);
138
+ React.useEffect(() => {
139
+ const model = editorRef.current?.getModel();
140
+ if (model && monacoRef.current)
141
+ monacoRef.current.editor.setModelLanguage(model, language);
142
+ }, [language, status]);
143
+ React.useEffect(() => {
144
+ editorRef.current?.updateOptions({ readOnly, ariaLabel: label });
145
+ }, [readOnly, label, status]);
146
+ return (_jsxs("div", { "data-slot": "code-editor", "data-status": status, className: cn("relative h-full min-h-24 w-full overflow-hidden bg-background", className), children: [_jsx("div", { ref: hostRef, className: cn("absolute inset-0", status !== "ready" && "invisible") }), status === "loading" && (_jsx(LoadingState, { className: "absolute inset-0", label: messages.workbench.editorLoading })), status === "fallback" && (_jsx("textarea", { "aria-label": label, value: value, readOnly: readOnly, spellCheck: false, onChange: (event) => onChange?.(event.target.value), onKeyDown: (event) => {
147
+ if (event.key === "Enter" && (event.metaKey || event.ctrlKey) && onRun) {
148
+ event.preventDefault();
149
+ const t = event.currentTarget;
150
+ onRun(t.value.slice(t.selectionStart, t.selectionEnd) || t.value);
151
+ }
152
+ }, className: "absolute inset-0 resize-none bg-background p-3 font-mono text-sm text-foreground outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset" }))] }));
153
+ }