@hienlh/ppm 0.13.8 → 0.13.10

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 (114) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/http-api.md +1 -1
  4. package/dist/web/assets/ai-settings-section-ysK_Eixc.js +1 -0
  5. package/dist/web/assets/architecture-PBZL5I3N-By4Nv3Gj.js +1 -0
  6. package/dist/web/assets/{audio-preview-DQbX8gfL.js → audio-preview-DISP-2AE.js} +1 -1
  7. package/dist/web/assets/{chat-tab-BJQT9kie.js → chat-tab-lp_mVSG-.js} +8 -8
  8. package/dist/web/assets/code-editor-BofKrbM8.js +8 -0
  9. package/dist/web/assets/{conflict-editor-BKwJLX0D.js → conflict-editor-kaAZUFD5.js} +1 -1
  10. package/dist/web/assets/csv-parser-Dly5nqE1.js +6 -0
  11. package/dist/web/assets/{csv-preview-D5lmgVEy.js → csv-preview-7TsYBQI6.js} +3 -3
  12. package/dist/web/assets/data-grid-overlay-editor-BjjuE4-G.js +1 -0
  13. package/dist/web/assets/data-grid-types-BTQHYBUh.js +1 -0
  14. package/dist/web/assets/database-DOWH9-Vv.js +1 -0
  15. package/dist/web/assets/database-viewer-P38Vxzkx.js +1 -0
  16. package/dist/web/assets/{diff-viewer-SAtaBwNI.js → diff-viewer-DlJfbgNJ.js} +1 -1
  17. package/dist/web/assets/dist-0kPgRaVx.js +1 -0
  18. package/dist/web/assets/{esm-nXReYVnB.js → esm-zjerHxpO.js} +1 -1
  19. package/dist/web/assets/{extension-webview-PiV4bKJ1.js → extension-webview-5s2MUx38.js} +1 -1
  20. package/dist/web/assets/gitGraph-HDMCJU4V-BLXEKVf1.js +1 -0
  21. package/dist/web/assets/glide-data-grid-D5D1N3L7.js +136 -0
  22. package/dist/web/assets/glide-data-grid-nthEL3fk.css +1 -0
  23. package/dist/web/assets/{image-preview-CbFFD9BS.js → image-preview-C9osjEPa.js} +1 -1
  24. package/dist/web/assets/index-COOnLKGB.css +2 -0
  25. package/dist/web/assets/index-CpcqiQOx.js +27 -0
  26. package/dist/web/assets/info-3K5VOQVL-CEkPcChg.js +1 -0
  27. package/dist/web/assets/{input-BMvRUOr7.js → input-ozrR2DAV.js} +1 -1
  28. package/dist/web/assets/keybindings-store-COxqSoML.js +1 -0
  29. package/dist/web/assets/{markdown-renderer-CHWA0KAo.js → markdown-renderer-k3EA9XmF.js} +3 -3
  30. package/dist/web/assets/number-overlay-editor-BoRxunFN.js +9 -0
  31. package/dist/web/assets/packet-RMMSAZCW-DECxYTOi.js +1 -0
  32. package/dist/web/assets/{pdf-preview-DQMdjqa2.js → pdf-preview-DvgyxJX7.js} +1 -1
  33. package/dist/web/assets/pie-UPGHQEXC-cjpNfVG5.js +1 -0
  34. package/dist/web/assets/{port-forwarding-tab-9BpNC9_7.js → port-forwarding-tab-CUkU6wac.js} +1 -1
  35. package/dist/web/assets/{postgres-viewer-Bm5T51n6.js → postgres-viewer-C1w0tqQw.js} +3 -3
  36. package/dist/web/assets/radar-KQ55EAFF-Dnpi068b.js +1 -0
  37. package/dist/web/assets/{settings-store-BHBb62gq.js → settings-store-B-OmHo3J.js} +1 -1
  38. package/dist/web/assets/settings-tab-D0zKyVwg.js +1 -0
  39. package/dist/web/assets/sql-query-editor-46hLU7MI.js +3 -0
  40. package/dist/web/assets/sqlite-viewer-DrLi8P6y.js +1 -0
  41. package/dist/web/assets/terminal-tab-DqA3fEoQ.js +1 -0
  42. package/dist/web/assets/treemap-KZPCXAKY-DRyb1eiw.js +1 -0
  43. package/dist/web/assets/{use-monaco-theme-CP-vyTF8.js → use-monaco-theme-DgzxiZS5.js} +1 -1
  44. package/dist/web/assets/{vendor-mermaid-CMiurk2b.js → vendor-mermaid-CCmA_6Y0.js} +3 -3
  45. package/dist/web/assets/{video-preview-BLI_RruT.js → video-preview-BU7tibc4.js} +1 -1
  46. package/dist/web/assets/x-CG-_0yIW.js +1 -0
  47. package/dist/web/index.html +13 -14
  48. package/dist/web/sw.js +1 -1
  49. package/package.json +2 -1
  50. package/src/web/components/database/data-grid.tsx +18 -2
  51. package/src/web/components/database/database-viewer.tsx +19 -8
  52. package/src/web/components/database/export-button.tsx +38 -18
  53. package/src/web/components/database/glide-column-search.tsx +81 -0
  54. package/src/web/components/database/glide-context-menu.tsx +95 -0
  55. package/src/web/components/database/glide-data-grid.tsx +207 -0
  56. package/src/web/components/database/glide-data-preview-panel.tsx +113 -0
  57. package/src/web/components/database/glide-grid-pagination.tsx +34 -0
  58. package/src/web/components/database/glide-grid-theme.ts +82 -0
  59. package/src/web/components/database/glide-grid-toolbar.tsx +105 -0
  60. package/src/web/components/database/glide-grid-types.ts +81 -0
  61. package/src/web/components/database/glide-header-menu.tsx +111 -0
  62. package/src/web/components/database/glide-save-bar.tsx +33 -0
  63. package/src/web/components/database/sql-query-editor.tsx +14 -4
  64. package/src/web/components/database/use-database.ts +10 -2
  65. package/src/web/components/database/use-glide-cell-content.ts +159 -0
  66. package/src/web/components/database/use-glide-columns.ts +69 -0
  67. package/src/web/components/database/use-glide-grid-actions.ts +164 -0
  68. package/src/web/components/database/use-glide-pending-edits.ts +72 -0
  69. package/src/web/components/database/use-glide-row-pinning.ts +35 -0
  70. package/src/web/components/database/use-glide-selection.ts +48 -0
  71. package/src/web/components/editor/code-editor.tsx +126 -7
  72. package/src/web/components/layout/editor-panel.tsx +2 -2
  73. package/src/web/components/sqlite/sqlite-viewer.tsx +21 -12
  74. package/src/web/components/sqlite/use-sqlite.ts +1 -1
  75. package/src/web/hooks/use-terminal.ts +1 -1
  76. package/src/web/index.html +1 -0
  77. package/test.sql +1 -0
  78. package/dist/web/assets/ai-settings-section-CHgpQ_OP.js +0 -1
  79. package/dist/web/assets/architecture-PBZL5I3N-WMbLpD5Y.js +0 -1
  80. package/dist/web/assets/code-editor-CeKTvfyz.js +0 -8
  81. package/dist/web/assets/csv-parser-DO0dz4x_.js +0 -6
  82. package/dist/web/assets/database-DCT0OjgQ.js +0 -1
  83. package/dist/web/assets/database-viewer-DixWWvjx.js +0 -5
  84. package/dist/web/assets/gitGraph-HDMCJU4V-BdPTuzO3.js +0 -1
  85. package/dist/web/assets/index-C1RBJe0a.css +0 -2
  86. package/dist/web/assets/index-ZFyltHwi.js +0 -27
  87. package/dist/web/assets/info-3K5VOQVL-MHX_1JfR.js +0 -1
  88. package/dist/web/assets/keybindings-store-D0C-Pq2o.js +0 -1
  89. package/dist/web/assets/packet-RMMSAZCW-CreFbf9A.js +0 -1
  90. package/dist/web/assets/pie-UPGHQEXC-CnaHXUh8.js +0 -1
  91. package/dist/web/assets/plus-51UQ45rf.js +0 -1
  92. package/dist/web/assets/radar-KQ55EAFF-UxsdRHvt.js +0 -1
  93. package/dist/web/assets/settings-tab-BUstSDLR.js +0 -1
  94. package/dist/web/assets/sql-completion-provider-tCzZfqWs.js +0 -1
  95. package/dist/web/assets/sql-query-editor-CMQpaOjA.js +0 -3
  96. package/dist/web/assets/sqlite-viewer-C7rhO4bn.js +0 -1
  97. package/dist/web/assets/terminal-tab-Xtj6RN0d.js +0 -1
  98. package/dist/web/assets/trash-2-CJYoLw7Q.js +0 -1
  99. package/dist/web/assets/treemap-KZPCXAKY-CBVPi4NV.js +0 -1
  100. package/dist/web/assets/x-BtqbfkR7.js +0 -1
  101. /package/dist/web/assets/{arrow-up-Dtrfv490.js → arrow-up-Rcw6_KKu.js} +0 -0
  102. /package/dist/web/assets/{chevron-right-BzAdxJRG.js → chevron-right-DnHIvvcy.js} +0 -0
  103. /package/dist/web/assets/{code-CuravVys.js → code-DGBecc50.js} +0 -0
  104. /package/dist/web/assets/{dist-D7KGU7Vl.js → dist-CaKCIxem.js} +0 -0
  105. /package/dist/web/assets/{dist-CGvx1c8C.js → dist-DGSkE2Ml.js} +0 -0
  106. /package/dist/web/assets/{katex-BFE6i_OH.js → katex-BuytEdO1.js} +0 -0
  107. /package/dist/web/assets/{lib-D_kRA9p6.js → lib-DQHnkzGy.js} +0 -0
  108. /package/dist/web/assets/{refresh-cw-CSFrDtiu.js → refresh-cw-LlbZDJpO.js} +0 -0
  109. /package/dist/web/assets/{scroll-area-BEllam7_.js → scroll-area-7H-Q_k8c.js} +0 -0
  110. /package/dist/web/assets/{sparkles-B0mRBy_j.js → sparkles-fWUT5Vzq.js} +0 -0
  111. /package/dist/web/assets/{table-Dq575bPF.js → table-tf7pRkME.js} +0 -0
  112. /package/dist/web/assets/{text-wrap-Cn6BNQfq.js → text-wrap-BV-R4Vvy.js} +0 -0
  113. /package/dist/web/assets/{use-blob-url-Hn6n1730.js → use-blob-url-e9uTXjv5.js} +0 -0
  114. /package/dist/web/assets/{vendor-xterm-u3AZMvTx.js → vendor-xterm-CU2c3f0A.js} +0 -0
@@ -0,0 +1,72 @@
1
+ import { useState, useCallback, useRef } from "react";
2
+
3
+ export interface PendingEdit {
4
+ pkVal: unknown;
5
+ col: string;
6
+ newVal: unknown;
7
+ }
8
+
9
+ interface UseGlidePendingEditsResult {
10
+ pendingEdits: Map<string, PendingEdit>;
11
+ pendingRef: React.RefObject<Map<string, PendingEdit>>;
12
+ addEdit: (pkVal: unknown, col: string, newVal: unknown) => void;
13
+ commitAll: () => Promise<void>;
14
+ discardAll: () => void;
15
+ hasPending: boolean;
16
+ pendingCount: number;
17
+ /** True after commitAll until cleared — used to clear edits on rows refresh */
18
+ committedRef: React.RefObject<boolean>;
19
+ }
20
+
21
+ /**
22
+ * Tracks cell edits locally until the user explicitly saves.
23
+ * After commit, keeps pending values visible until rows refresh (avoids flash of stale data).
24
+ * Supports inline insert: edits with PK starting "__new_" are routed to onInsertRow.
25
+ */
26
+ export function useGlidePendingEdits(
27
+ pkCol: string | null,
28
+ onCellUpdate: (pkCol: string, pkVal: unknown, col: string, val: unknown) => void,
29
+ onInsertRow?: (values: Record<string, unknown>) => Promise<void>,
30
+ ): UseGlidePendingEditsResult {
31
+ const [pendingEdits, setPendingEdits] = useState<Map<string, PendingEdit>>(new Map());
32
+ const pendingRef = useRef(pendingEdits);
33
+ pendingRef.current = pendingEdits;
34
+ const committedRef = useRef(false);
35
+
36
+ const addEdit = useCallback((pkVal: unknown, col: string, newVal: unknown) => {
37
+ const key = `${pkVal}:${col}`;
38
+ setPendingEdits((prev) => new Map(prev).set(key, { pkVal, col, newVal }));
39
+ }, []);
40
+
41
+ const commitAll = useCallback(async () => {
42
+ if (!pkCol) return;
43
+ const newRows = new Map<string, Record<string, unknown>>();
44
+ for (const edit of pendingRef.current.values()) {
45
+ const pkStr = String(edit.pkVal);
46
+ if (pkStr.startsWith("__new_")) {
47
+ if (!newRows.has(pkStr)) newRows.set(pkStr, {});
48
+ newRows.get(pkStr)![edit.col] = edit.newVal;
49
+ } else {
50
+ onCellUpdate(pkCol, edit.pkVal, edit.col, edit.newVal);
51
+ }
52
+ }
53
+ if (onInsertRow) {
54
+ for (const values of newRows.values()) {
55
+ await onInsertRow(values);
56
+ }
57
+ }
58
+ committedRef.current = true;
59
+ // Don't clear — wait for rows prop to refresh so grid doesn't flash stale data
60
+ }, [pkCol, onCellUpdate, onInsertRow]);
61
+
62
+ const discardAll = useCallback(() => {
63
+ setPendingEdits(new Map());
64
+ committedRef.current = false;
65
+ }, []);
66
+
67
+ return {
68
+ pendingEdits, pendingRef, addEdit, commitAll, discardAll,
69
+ hasPending: pendingEdits.size > 0, pendingCount: pendingEdits.size,
70
+ committedRef,
71
+ };
72
+ }
@@ -0,0 +1,35 @@
1
+ import { useState, useMemo } from "react";
2
+
3
+ interface UseGlideRowPinningResult {
4
+ /** Rows reordered: unpinned first, pinned at end (frozen via freezeTrailingRows) */
5
+ effectiveRows: Record<string, unknown>[];
6
+ /** Number of pinned rows — pass to DataEditor's freezeTrailingRows */
7
+ pinnedCount: number;
8
+ pinnedPks: Set<string>;
9
+ setPinnedPks: React.Dispatch<React.SetStateAction<Set<string>>>;
10
+ }
11
+
12
+ /**
13
+ * Manages row pinning state — pinned rows placed at the end of the array
14
+ * so they can be frozen at the bottom via Glide's freezeTrailingRows.
15
+ */
16
+ export function useGlideRowPinning(
17
+ rows: Record<string, unknown>[],
18
+ pkCol: string | null,
19
+ ): UseGlideRowPinningResult {
20
+ const [pinnedPks, setPinnedPks] = useState<Set<string>>(new Set());
21
+
22
+ const effectiveRows = useMemo(() => {
23
+ if (pinnedPks.size === 0 || !pkCol) return rows;
24
+ const normal: Record<string, unknown>[] = [];
25
+ const pinned: Record<string, unknown>[] = [];
26
+ for (const row of rows) {
27
+ if (pinnedPks.has(String(row[pkCol] ?? ""))) pinned.push(row); else normal.push(row);
28
+ }
29
+ return [...normal, ...pinned];
30
+ }, [rows, pinnedPks, pkCol]);
31
+
32
+ const pinnedCount = pinnedPks.size;
33
+
34
+ return { effectiveRows, pinnedCount, pinnedPks, setPinnedPks };
35
+ }
@@ -0,0 +1,48 @@
1
+ import { useState, useCallback, useMemo } from "react";
2
+ import { CompactSelection, type GridSelection } from "@glideapps/glide-data-grid";
3
+
4
+ const EMPTY_SELECTION: GridSelection = {
5
+ columns: CompactSelection.empty(),
6
+ rows: CompactSelection.empty(),
7
+ };
8
+
9
+ interface UseGlideSelectionResult {
10
+ gridSelection: GridSelection;
11
+ onGridSelectionChange: (newSel: GridSelection) => void;
12
+ /** Array of selected row indices (derived from CompactSelection) */
13
+ selectedRowIndices: number[];
14
+ clearSelection: () => void;
15
+ }
16
+
17
+ /**
18
+ * Manages controlled selection state for Glide Data Grid.
19
+ * Provides row indices array for bulk operations (delete, export).
20
+ */
21
+ export function useGlideSelection(): UseGlideSelectionResult {
22
+ const [gridSelection, setGridSelection] = useState<GridSelection>(EMPTY_SELECTION);
23
+
24
+ const onGridSelectionChange = useCallback((newSel: GridSelection) => {
25
+ setGridSelection(newSel);
26
+ }, []);
27
+
28
+ const selectedRowIndices = useMemo(() => {
29
+ const indices: number[] = [];
30
+ if (gridSelection.rows) {
31
+ for (const range of gridSelection.rows) {
32
+ // CompactSelection stores [start, end) ranges
33
+ if (Array.isArray(range)) {
34
+ for (let i = range[0]; i < range[1]; i++) indices.push(i);
35
+ } else {
36
+ indices.push(range);
37
+ }
38
+ }
39
+ }
40
+ return indices;
41
+ }, [gridSelection.rows]);
42
+
43
+ const clearSelection = useCallback(() => {
44
+ setGridSelection(EMPTY_SELECTION);
45
+ }, []);
46
+
47
+ return { gridSelection, onGridSelectionChange, selectedRowIndices, clearSelection };
48
+ }
@@ -8,13 +8,16 @@ import { usePanelStore } from "@/stores/panel-store";
8
8
  import { useSettingsStore } from "@/stores/settings-store";
9
9
  import { basename } from "@/lib/utils";
10
10
  import { useMonacoTheme } from "@/lib/use-monaco-theme";
11
- import { Loader2, FileWarning, Play, Database } from "lucide-react";
11
+ import { Loader2, FileWarning, Play, Database, ExternalLink, X, GripHorizontal } from "lucide-react";
12
12
  import { EditorBreadcrumb } from "./editor-breadcrumb";
13
13
  import { EditorToolbar } from "./editor-toolbar";
14
14
  import { SaveAsDialog } from "./save-as-dialog";
15
15
  import { EditorMobileToolbar } from "./editor-mobile-toolbar";
16
16
  import { createSqlCompletionProvider, clearCompletionCache, type SchemaInfo } from "../database/sql-completion-provider";
17
17
  import { useConnections, type Connection } from "../database/use-connections";
18
+ import { GlideDataGrid } from "../database/glide-data-grid";
19
+ import type { GridColumnSchema } from "../database/glide-grid-types";
20
+ import type { DbQueryResult } from "../database/use-database";
18
21
 
19
22
  const MarkdownRenderer = lazy(() =>
20
23
  import("@/components/shared/markdown-renderer").then((m) => ({ default: m.MarkdownRenderer }))
@@ -170,18 +173,37 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
170
173
  return () => { completionDisposable.current?.dispose(); };
171
174
  }, [sqlSchemaInfo]);
172
175
 
173
- // Run in DB Viewer
176
+ // Run SQL inline — execute query and show results in bottom panel
174
177
  const openTab = useTabStore((s) => s.openTab);
175
- const runSqlInViewer = useCallback((sqlText: string) => {
178
+ const [sqlResult, setSqlResult] = useState<DbQueryResult | null>(null);
179
+ const [sqlError, setSqlError] = useState<string | null>(null);
180
+ const [sqlLoading, setSqlLoading] = useState(false);
181
+ const [sqlResultSql, setSqlResultSql] = useState<string>("");
182
+ const runSqlInViewer = useCallback(async (sqlText: string) => {
176
183
  if (!selectedSqlConn) return;
184
+ setSqlLoading(true);
185
+ setSqlError(null);
186
+ setSqlResultSql(sqlText);
187
+ try {
188
+ const result = await api.post<DbQueryResult>(`/api/db/connections/${selectedSqlConn.id}/query`, { sql: sqlText });
189
+ setSqlResult(result);
190
+ } catch (e) {
191
+ setSqlError((e as Error).message);
192
+ setSqlResult(null);
193
+ } finally {
194
+ setSqlLoading(false);
195
+ }
196
+ }, [selectedSqlConn]);
197
+ const openSqlResultInTab = useCallback(() => {
198
+ if (!selectedSqlConn || !sqlResultSql) return;
177
199
  openTab({
178
200
  type: "database",
179
201
  title: `${selectedSqlConn.name} · Query`,
180
202
  projectId: null,
181
203
  closable: true,
182
- metadata: { connectionId: selectedSqlConn.id, connectionName: selectedSqlConn.name, dbType: selectedSqlConn.type, initialSql: sqlText },
204
+ metadata: { connectionId: selectedSqlConn.id, connectionName: selectedSqlConn.name, dbType: selectedSqlConn.type, initialSql: sqlResultSql },
183
205
  });
184
- }, [selectedSqlConn, openTab]);
206
+ }, [selectedSqlConn, openTab, sqlResultSql]);
185
207
 
186
208
  const handleRunInDbViewer = useCallback(() => {
187
209
  if (!editorRef.current || !selectedSqlConn) return;
@@ -520,7 +542,7 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
520
542
  onClick={handleRunInDbViewer}
521
543
  disabled={!selectedSqlConn}
522
544
  className="p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors"
523
- title="Run all in DB Viewer"
545
+ title="Run SQL"
524
546
  >
525
547
  <Play className="size-3.5" />
526
548
  </button>
@@ -583,7 +605,7 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
583
605
  ) : isMarkdown && mdMode === "preview" ? (
584
606
  <MarkdownPreview content={content ?? ""} />
585
607
  ) : (
586
- <div className="flex-1 overflow-hidden">
608
+ <div className="flex-1 overflow-hidden min-h-0">
587
609
  <Editor
588
610
  height="100%"
589
611
  language={inlineLanguage ?? getMonacoLanguage(filePath ?? "")}
@@ -608,6 +630,16 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
608
630
  </div>
609
631
  )}
610
632
 
633
+ {/* Inline SQL result panel */}
634
+ {isSql && (sqlResult || sqlError || sqlLoading) && (
635
+ <SqlResultPanel
636
+ result={sqlResult} error={sqlError} loading={sqlLoading}
637
+ connName={selectedSqlConn?.name}
638
+ onClose={() => { setSqlResult(null); setSqlError(null); setSqlLoading(false); }}
639
+ onOpenInTab={openSqlResultInTab}
640
+ />
641
+ )}
642
+
611
643
  {/* Mobile toolbar — bottom, like terminal */}
612
644
  {isMobile && <EditorMobileToolbar editorRef={editorRef} readOnly={inlineContent != null} />}
613
645
 
@@ -625,6 +657,93 @@ export const CodeEditor = memo(function CodeEditor({ metadata, tabId }: CodeEdit
625
657
  );
626
658
  });
627
659
 
660
+ const NOOP = () => {};
661
+
662
+ /** Inline SQL result panel — shows query results below the editor */
663
+ function SqlResultPanel({ result, error, loading, connName, onClose, onOpenInTab }: {
664
+ result: DbQueryResult | null;
665
+ error: string | null;
666
+ loading: boolean;
667
+ connName?: string;
668
+ onClose: () => void;
669
+ onOpenInTab: () => void;
670
+ }) {
671
+ const tableData = useMemo(() => (
672
+ result?.changeType === "select" && result.rows.length > 0
673
+ ? { columns: result.columns, rows: result.rows, total: result.rows.length, limit: result.rows.length }
674
+ : null
675
+ ), [result]);
676
+
677
+ const querySchema = useMemo<GridColumnSchema[]>(() => (
678
+ (result?.columns ?? []).map((c) => ({ name: c, type: "text", nullable: true, pk: false, defaultValue: null }))
679
+ ), [result?.columns]);
680
+
681
+ const [panelHeight, setPanelHeight] = useState(250);
682
+ const handleDrag = useCallback((e: React.MouseEvent) => {
683
+ e.preventDefault();
684
+ const startY = e.clientY;
685
+ const startH = panelHeight;
686
+ const onMove = (ev: MouseEvent) => setPanelHeight(Math.max(80, startH + (startY - ev.clientY)));
687
+ const onUp = () => { document.removeEventListener("mousemove", onMove); document.removeEventListener("mouseup", onUp); };
688
+ document.addEventListener("mousemove", onMove);
689
+ document.addEventListener("mouseup", onUp);
690
+ }, [panelHeight]);
691
+
692
+ return (
693
+ <div className="shrink-0 border-t border-border flex flex-col" style={{ height: panelHeight }}>
694
+ {/* Resize handle */}
695
+ <div onMouseDown={handleDrag}
696
+ className="shrink-0 h-1.5 cursor-row-resize bg-border/50 hover:bg-primary/30 flex items-center justify-center transition-colors">
697
+ <GripHorizontal className="size-3 text-muted-foreground/50" />
698
+ </div>
699
+ {/* Title bar */}
700
+ <div className="flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border shrink-0">
701
+ <Database className="size-3 text-muted-foreground" />
702
+ <span className="text-xs font-medium text-foreground truncate flex-1">
703
+ {connName ? `${connName} · Results` : "Query Results"}
704
+ {result?.executionTimeMs != null && <span className="text-muted-foreground ml-1.5 font-normal">{result.executionTimeMs}ms</span>}
705
+ </span>
706
+ <button type="button" onClick={onOpenInTab} title="Open in DB Viewer tab"
707
+ className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-muted-foreground hover:text-foreground hover:bg-muted transition-colors">
708
+ <ExternalLink className="size-3" />
709
+ <span className="hidden sm:inline">Open in Tab</span>
710
+ </button>
711
+ <button type="button" onClick={onClose} title="Close results"
712
+ className="p-0.5 rounded text-muted-foreground hover:text-foreground transition-colors">
713
+ <X className="size-3" />
714
+ </button>
715
+ </div>
716
+
717
+ {/* Content */}
718
+ <div className="flex-1 overflow-hidden min-h-0">
719
+ {loading && (
720
+ <div className="flex items-center justify-center h-full">
721
+ <Loader2 className="size-4 animate-spin text-muted-foreground" />
722
+ </div>
723
+ )}
724
+ {error && <div className="px-3 py-2 text-xs text-destructive bg-destructive/5">{error}</div>}
725
+ {result?.changeType === "modify" && (
726
+ <div className="px-3 py-2 text-xs text-green-500">
727
+ {result.rowsAffected} row(s) affected
728
+ </div>
729
+ )}
730
+ {tableData && (
731
+ <GlideDataGrid
732
+ columns={tableData.columns} rows={tableData.rows} total={tableData.total} limit={tableData.limit}
733
+ schema={querySchema} loading={false}
734
+ page={1} onPageChange={NOOP} onCellUpdate={NOOP}
735
+ orderBy={null} orderDir="ASC" onToggleSort={NOOP}
736
+ connectionName={connName}
737
+ />
738
+ )}
739
+ {result?.changeType === "select" && result.rows.length === 0 && (
740
+ <div className="px-3 py-2 text-xs text-muted-foreground">No results</div>
741
+ )}
742
+ </div>
743
+ </div>
744
+ );
745
+ }
746
+
628
747
  function LoadingSpinner() {
629
748
  return <div className="flex items-center justify-center h-full"><Loader2 className="size-5 animate-spin text-text-subtle" /></div>;
630
749
  }
@@ -67,13 +67,13 @@ export function EditorPanel({ panelId, projectName }: EditorPanelProps) {
67
67
  const isActive = tab.id === panel.activeTabId;
68
68
  if (!Component) {
69
69
  return (
70
- <div key={tab.id} className={isActive ? "h-full w-full flex items-center justify-center text-muted-foreground" : "hidden"}>
70
+ <div key={tab.id} className={isActive ? "absolute inset-0 flex items-center justify-center text-muted-foreground" : "hidden"}>
71
71
  Unknown tab type: {tab.type}
72
72
  </div>
73
73
  );
74
74
  }
75
75
  return (
76
- <div key={tab.id} className={isActive ? "h-full w-full" : "hidden"}>
76
+ <div key={tab.id} className="absolute inset-0" style={isActive ? undefined : { opacity: 0, pointerEvents: "none" }}>
77
77
  <Suspense fallback={<div className="flex items-center justify-center h-full"><Loader2 className="size-6 animate-spin text-primary" /></div>}>
78
78
  <Component metadata={tab.metadata} tabId={tab.id} />
79
79
  </Suspense>
@@ -1,8 +1,8 @@
1
- import { useState, useEffect, useRef } from "react";
1
+ import { useState, useEffect, useRef, useCallback } from "react";
2
2
  import { Database, Loader2, AlertCircle } from "lucide-react";
3
3
  import { useSqlite } from "./use-sqlite";
4
4
  import { SqliteTableList } from "./sqlite-table-list";
5
- import { SqliteDataGrid } from "./sqlite-data-grid";
5
+ import { GlideDataGrid } from "../database/glide-data-grid";
6
6
  import { SqliteQueryEditor } from "./sqlite-query-editor";
7
7
 
8
8
  interface SqliteViewerProps {
@@ -118,17 +118,26 @@ function SqliteViewerInner({
118
118
  </div>
119
119
  </div>
120
120
 
121
- {/* Data grid */}
121
+ {/* Data grid — adapter from sqlite rowid-based API to GlideDataGrid's pk-based API */}
122
122
  <div className={`flex-1 overflow-hidden ${queryPanelOpen ? "max-h-[60%]" : ""}`}>
123
- <SqliteDataGrid
124
- tableData={sqlite.tableData}
125
- schema={sqlite.schema}
126
- loading={sqlite.loading}
127
- page={sqlite.page}
128
- onPageChange={sqlite.setPage}
129
- onCellUpdate={sqlite.updateCell}
130
- onRowDelete={sqlite.deleteRow}
131
- />
123
+ {sqlite.tableData ? (
124
+ <GlideDataGrid
125
+ columns={sqlite.tableData.columns}
126
+ rows={sqlite.tableData.rows}
127
+ total={sqlite.tableData.total}
128
+ limit={sqlite.tableData.limit}
129
+ schema={sqlite.schema.map((c) => ({ name: c.name, type: c.type, nullable: !c.notnull, pk: !!c.pk, defaultValue: c.dflt_value, fk: c.fk ?? null }))}
130
+ loading={sqlite.loading}
131
+ page={sqlite.page}
132
+ onPageChange={sqlite.setPage}
133
+ onCellUpdate={(_pkCol, pkVal, col, val) => sqlite.updateCell(pkVal as number, col, val)}
134
+ onRowDelete={(_pkCol, pkVal) => sqlite.deleteRow(pkVal as number)}
135
+ />
136
+ ) : (
137
+ <div className="flex items-center justify-center h-full text-xs text-muted-foreground">
138
+ {sqlite.loading ? <Loader2 className="size-4 animate-spin" /> : "Select a table"}
139
+ </div>
140
+ )}
132
141
  </div>
133
142
 
134
143
  {/* Query editor (collapsible) */}
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from "react";
2
2
  import { api, projectUrl } from "@/lib/api-client";
3
3
 
4
4
  export interface TableInfo { name: string; rowCount: number }
5
- export interface ColumnInfo { cid: number; name: string; type: string; notnull: boolean; pk: boolean; dflt_value: string | null }
5
+ export interface ColumnInfo { cid: number; name: string; type: string; notnull: boolean; pk: boolean; dflt_value: string | null; fk?: { table: string; column: string } | null }
6
6
  export interface QueryResult { columns: string[]; rows: Record<string, unknown>[]; rowsAffected: number; changeType: "select" | "modify"; executionTimeMs?: number }
7
7
  interface TableData { columns: string[]; rows: Record<string, unknown>[]; total: number; page: number; limit: number }
8
8
 
@@ -147,7 +147,7 @@ export function useTerminal(
147
147
  if (event.data.startsWith("{")) {
148
148
  try {
149
149
  const msg = JSON.parse(event.data);
150
- if (msg.type === "session" || msg.type === "error" || msg.type === "exited") {
150
+ if (msg.type === "session" || msg.type === "error" || msg.type === "exited" || msg.type === "ping") {
151
151
  if (msg.type === "session" && msg.id) {
152
152
  actualSessionId.current = msg.id; // Save for reconnect
153
153
  }
@@ -11,6 +11,7 @@
11
11
  <link href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@400;500;600;700&family=Geist:wght@400;500;600;700&display=swap" rel="stylesheet" />
12
12
  </head>
13
13
  <body class="bg-[#0f1419] text-[#e5e7eb] font-sans antialiased">
14
+ <div id="portal" style="position: fixed; left: 0; top: 0; z-index: 9999;" /></div>
14
15
  <div id="root"></div>
15
16
  <script type="module" src="./main.tsx"></script>
16
17
  </body>
package/test.sql ADDED
@@ -0,0 +1 @@
1
+ select * from config;
@@ -1 +0,0 @@
1
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import{D as r,_ as i,b as a,c as o,d as s,f as c,g as l,h as u,m as d,p as f,s as p,u as m,v as h,x as g,y as _}from"./vendor-ui-B-89Uj8i.js";import{t as v}from"./createLucideIcon-BjHrJDVb.js";import{i as y}from"./dist-D7KGU7Vl.js";import{n as b,r as x}from"./plus-51UQ45rf.js";import{t as S}from"./input-BMvRUOr7.js";import{t as C}from"./refresh-cw-CSFrDtiu.js";import{t as w}from"./trash-2-CJYoLw7Q.js";import{i as T,t as E}from"./api-client-Dvzcc_EO.js";import{n as D}from"./utils-CTg5uAYR.js";import{a as O,h as k}from"./api-settings-D0_eiIYv.js";var A=v(`bell-off`,[[`path`,{d:`M10.268 21a2 2 0 0 0 3.464 0`,key:`vwvbt9`}],[`path`,{d:`M17 17H4a1 1 0 0 1-.74-1.673C4.59 13.956 6 12.499 6 8a6 6 0 0 1 .258-1.742`,key:`178tsu`}],[`path`,{d:`m2 2 20 20`,key:`1ooewy`}],[`path`,{d:`M8.668 3.01A6 6 0 0 1 18 8c0 2.687.77 4.653 1.707 6.05`,key:`1hqiys`}]]),j=v(`bot`,[[`path`,{d:`M12 8V4H8`,key:`hb8ula`}],[`rect`,{width:`16`,height:`12`,x:`4`,y:`8`,rx:`2`,key:`enze0r`}],[`path`,{d:`M2 14h2`,key:`vft8re`}],[`path`,{d:`M20 14h2`,key:`4cs60a`}],[`path`,{d:`M15 13v2`,key:`1xurst`}],[`path`,{d:`M9 13v2`,key:`rq6x2g`}]]),M=v(`bug`,[[`path`,{d:`M12 20v-9`,key:`1qisl0`}],[`path`,{d:`M14 7a4 4 0 0 1 4 4v3a6 6 0 0 1-12 0v-3a4 4 0 0 1 4-4z`,key:`uouzyp`}],[`path`,{d:`M14.12 3.88 16 2`,key:`qol33r`}],[`path`,{d:`M21 21a4 4 0 0 0-3.81-4`,key:`1b0z45`}],[`path`,{d:`M21 5a4 4 0 0 1-3.55 3.97`,key:`5cxbf6`}],[`path`,{d:`M22 13h-4`,key:`1jl80f`}],[`path`,{d:`M3 21a4 4 0 0 1 3.81-4`,key:`1fjd4g`}],[`path`,{d:`M3 5a4 4 0 0 0 3.55 3.97`,key:`1d7oge`}],[`path`,{d:`M6 13H2`,key:`82j7cp`}],[`path`,{d:`m8 2 1.88 1.88`,key:`fmnt4t`}],[`path`,{d:`M9 7.13V6a3 3 0 1 1 6 0v1.13`,key:`1vgav8`}]]),N=v(`lock`,[[`rect`,{width:`18`,height:`11`,x:`3`,y:`11`,rx:`2`,ry:`2`,key:`1w4ew1`}],[`path`,{d:`M7 11V7a5 5 0 0 1 10 0v4`,key:`fwvmzm`}]]),P=v(`pencil`,[[`path`,{d:`M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z`,key:`1a8usu`}],[`path`,{d:`m15 5 4 4`,key:`1mk7zo`}]]),F=e(n(),1),I=t();function L({className:e,...t}){return(0,I.jsx)(r,{"data-slot":`label`,className:D(`flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50`,e),...t})}var R=F.forwardRef(({className:e,...t},n)=>(0,I.jsx)(p,{className:D(`peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input`,e),...t,ref:n,children:(0,I.jsx)(o,{className:D(`pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0`)})}));R.displayName=p.displayName;function z({...e}){return(0,I.jsx)(l,{"data-slot":`select`,...e})}function B({...e}){return(0,I.jsx)(a,{"data-slot":`select-value`,...e})}function V({className:e,size:t=`default`,children:n,...r}){return(0,I.jsxs)(_,{"data-slot":`select-trigger`,"data-size":t,className:D(`flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground`,e),...r,children:[n,(0,I.jsx)(s,{asChild:!0,children:(0,I.jsx)(x,{className:`size-4 opacity-50`})})]})}function H({className:e,children:t,position:n=`item-aligned`,align:r=`center`,...i}){return(0,I.jsx)(u,{children:(0,I.jsxs)(m,{"data-slot":`select-content`,className:D(`relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95`,n===`popper`&&`data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1`,e),position:n,align:r,...i,children:[(0,I.jsx)(W,{}),(0,I.jsx)(g,{className:D(`p-1`,n===`popper`&&`h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1`),children:t}),(0,I.jsx)(G,{})]})})}function U({className:e,children:t,...n}){return(0,I.jsxs)(c,{"data-slot":`select-item`,className:D(`relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2`,e),...n,children:[(0,I.jsx)(`span`,{"data-slot":`select-item-indicator`,className:`absolute right-2 flex size-3.5 items-center justify-center`,children:(0,I.jsx)(f,{children:(0,I.jsx)(y,{className:`size-4`})})}),(0,I.jsx)(d,{children:t})]})}function W({className:e,...t}){return(0,I.jsx)(h,{"data-slot":`select-scroll-up-button`,className:D(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,I.jsx)(b,{className:`size-4`})})}function G({className:e,...t}){return(0,I.jsx)(i,{"data-slot":`select-scroll-down-button`,className:D(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,I.jsx)(x,{className:`size-4`})})}var K={claude:`C`,cursor:`▶`,codex:`◆`,gemini:`G`};function q({value:e,onChange:t,projectName:n}){let[r,i]=(0,F.useState)([]),[a,o]=(0,F.useState)(!1),s=(0,F.useRef)(null),c=(0,F.useRef)(0);(0,F.useEffect)(()=>{n&&E.get(`${T(n)}/chat/providers`).then(i).catch(()=>{})},[n]),(0,F.useEffect)(()=>{if(!a)return;let e=e=>{s.current&&!s.current.contains(e.target)&&o(!1)};return document.addEventListener(`mousedown`,e),()=>document.removeEventListener(`mousedown`,e)},[a]),(0,F.useEffect)(()=>{a&&(c.current=Math.max(0,r.findIndex(t=>t.id===e)))},[a,e,r]);let l=(0,F.useCallback)(e=>{if(e.key===`Escape`){o(!1);return}if(e.key===`ArrowDown`||e.key===`ArrowUp`){e.preventDefault();let t=e.key===`ArrowDown`?1:-1;c.current=(c.current+t+r.length)%r.length,(s.current?.querySelector(`[data-idx="${c.current}"]`))?.focus()}if(e.key===`Enter`){e.preventDefault();let n=r[c.current];n&&(t(n.id),o(!1))}},[t,r]);if(r.length<=1)return null;let u=r.find(t=>t.id===e),d=K[e]||`?`;return(0,I.jsxs)(`div`,{className:`relative`,children:[(0,I.jsxs)(`button`,{type:`button`,onClick:e=>{e.stopPropagation(),o(e=>!e)},className:`inline-flex items-center gap-1 px-2 py-1 rounded-md text-[11px] text-text-subtle hover:text-text-primary hover:bg-surface-elevated transition-colors border border-transparent hover:border-border`,"aria-label":`AI Provider: ${u?.name??e}`,children:[(0,I.jsx)(`span`,{className:`inline-flex h-3.5 w-3.5 items-center justify-center rounded text-[9px] font-bold bg-surface-elevated shrink-0`,children:d}),(0,I.jsx)(`span`,{className:`max-w-[80px] truncate capitalize`,children:u?.name??e})]}),a&&(0,I.jsxs)(`div`,{ref:s,role:`listbox`,"aria-label":`AI Providers`,onKeyDown:l,onMouseDown:e=>e.stopPropagation(),onClick:e=>e.stopPropagation(),className:`absolute bottom-full left-0 mb-1 z-50 w-56 rounded-lg border border-border bg-surface shadow-lg`,children:[(0,I.jsx)(`div`,{className:`px-3 py-2 border-b border-border`,children:(0,I.jsx)(`span`,{className:`text-xs font-medium text-text-secondary`,children:`Provider`})}),(0,I.jsx)(`div`,{className:`py-1`,children:r.map((n,r)=>{let i=K[n.id]||`?`,a=n.id===e;return(0,I.jsxs)(`button`,{"data-idx":r,role:`option`,"aria-selected":a,tabIndex:0,onClick:()=>{t(n.id),o(!1)},className:`w-full flex items-center gap-3 px-3 py-2 text-left transition-colors hover:bg-surface-elevated focus:bg-surface-elevated focus:outline-none ${a?`bg-surface-elevated`:``}`,children:[(0,I.jsx)(`span`,{className:`inline-flex h-5 w-5 items-center justify-center rounded text-[11px] font-bold bg-surface-elevated text-text-subtle shrink-0`,children:i}),(0,I.jsx)(`span`,{className:`flex-1 text-sm font-medium text-text-primary capitalize`,children:n.name}),a&&(0,I.jsx)(y,{className:`size-4 shrink-0 text-primary`})]},n.id)})})]})]})}function J({providerId:e}){return(0,I.jsx)(`span`,{className:`inline-flex h-4 w-4 items-center justify-center rounded text-[10px] font-bold bg-surface-elevated text-text-subtle shrink-0`,title:e,children:K[e]||`?`})}var Y=[{value:`low`,label:`Low`},{value:`medium`,label:`Medium`},{value:`high`,label:`High`}],X=[{value:`bypassPermissions`,label:`Bypass permissions (default)`},{value:`default`,label:`Ask before edits`},{value:`acceptEdits`,label:`Edit automatically`},{value:`plan`,label:`Plan mode`}],Z={claude:`Claude`,cursor:`Cursor`,codex:`Codex`,gemini:`Gemini`};function Q({compact:e}={}){let[t,n]=(0,F.useState)(null),[r,i]=(0,F.useState)(``),[a,o]=(0,F.useState)([]),[s,c]=(0,F.useState)(!1),[l,u]=(0,F.useState)(!1),[d,f]=(0,F.useState)(null),[p,m]=(0,F.useState)(0);(0,F.useEffect)(()=>{O().then(e=>{n(e),i(e.default_provider??`claude`)}).catch(e=>f(e.message))},[]),(0,F.useEffect)(()=>{r&&(c(!0),E.get(`/api/settings/ai/providers/${r}/models`).then(o).catch(()=>o([])).finally(()=>c(!1)))},[r]);let h=t?Object.keys(t.providers).filter(e=>e!==`mock`).map(e=>({id:e,name:Z[e]??e})):[],g=t?.providers[r],_=g?.type===`agent-sdk`||!g?.type&&r===`claude`,v=async(e,i)=>{if(t){u(!0),f(null);try{n(await k({providers:{[r]:{[e]:i}}})),m(e=>e+1)}catch(e){f(e.message)}finally{u(!1)}}},y=e?`text-[11px]`:`text-sm`,b=e?`text-xs`:`text-sm`,x=e?`space-y-2`:`space-y-4`,C=e?`space-y-1.5`:`space-y-3`,w=e?`space-y-1`:`space-y-1.5`;if(!t)return(0,I.jsxs)(`div`,{className:C,children:[(0,I.jsx)(`h3`,{className:`${b} font-medium text-text-secondary`,children:`AI Settings`}),(0,I.jsx)(`p`,{className:`${y} text-text-subtle`,children:d?`Error: ${d}`:`Loading...`})]});let T=_?a:[{value:`__default__`,label:`Auto (default)`},...a];return(0,I.jsxs)(`div`,{className:x,children:[(0,I.jsx)(`h3`,{className:`${b} font-medium text-text-secondary`,children:`AI Settings`}),h.length>1&&(0,I.jsx)(`div`,{className:`flex gap-0.5 border-b border-border/50 -mx-1 px-1`,children:h.map(e=>(0,I.jsxs)(`button`,{onClick:()=>i(e.id),className:`flex items-center gap-1 px-2 py-1 text-[11px] rounded-t transition-colors ${r===e.id?`text-primary border-b-2 border-primary font-medium`:`text-text-subtle hover:text-text-secondary`}`,children:[(0,I.jsx)(J,{providerId:e.id}),(0,I.jsx)(`span`,{className:`capitalize`,children:e.name})]},e.id))}),(0,I.jsxs)(`div`,{className:C,children:[a.length>0&&(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-model`,className:e?y:void 0,children:`Model`}),(0,I.jsxs)(z,{value:_?g?.model??a[0]?.value:g?.model||`__default__`,onValueChange:e=>v(`model`,e===`__default__`?void 0:e),disabled:s,children:[(0,I.jsx)(V,{id:`ai-model`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,I.jsx)(B,{placeholder:s?`Loading models...`:`Select model`})}),(0,I.jsx)(H,{className:`max-h-[300px]`,children:T.map(e=>(0,I.jsx)(U,{value:e.value,children:e.label},e.value))})]})]}),_&&(0,I.jsxs)(I.Fragment,{children:[(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-base-url`,className:e?y:void 0,children:`Base URL`}),(0,I.jsx)(S,{id:`ai-base-url`,type:`url`,defaultValue:g?.base_url??``,placeholder:`https://api.anthropic.com (default)`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{v(`base_url`,e.target.value.trim()||void 0)}},`baseurl-${r}-${p}`)]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-api-key`,className:e?y:void 0,children:`API Key / Token`}),(0,I.jsx)(S,{id:`ai-api-key`,type:`password`,defaultValue:g?.api_key??``,placeholder:`sk-ant-... (optional, overrides accounts)`,className:e?`h-7 text-[11px] font-mono`:`font-mono`,onBlur:e=>{let t=e.target.value.trim();t.startsWith(`••••`)||v(`api_key`,t||void 0)}},`apikey-${r}-${p}`),(0,I.jsx)(`p`,{className:`${e?`text-[9px]`:`text-[11px]`} text-muted-foreground`,children:`Direct API key or OAuth token. Leave empty to use connected accounts.`})]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-effort`,className:e?y:void 0,children:`Effort`}),(0,I.jsxs)(z,{value:g?.effort??`high`,onValueChange:e=>v(`effort`,e),children:[(0,I.jsx)(V,{id:`ai-effort`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,I.jsx)(B,{})}),(0,I.jsx)(H,{children:Y.map(e=>(0,I.jsx)(U,{value:e.value,children:e.label},e.value))})]})]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-max-turns`,className:e?y:void 0,children:`Max Turns (1-500)`}),(0,I.jsx)(S,{id:`ai-max-turns`,type:`number`,min:1,max:500,defaultValue:g?.max_turns??100,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseInt(e.target.value);isNaN(t)||v(`max_turns`,t)}},`turns-${r}-${p}`)]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-budget`,className:e?y:void 0,children:`Max Budget (USD)`}),(0,I.jsx)(S,{id:`ai-budget`,type:`number`,step:.1,min:.01,max:50,defaultValue:g?.max_budget_usd??``,placeholder:`No limit`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseFloat(e.target.value);v(`max_budget_usd`,isNaN(t)?void 0:t)}},`budget-${r}-${p}`)]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-thinking`,className:e?y:void 0,children:`Thinking Budget (tokens)`}),(0,I.jsx)(S,{id:`ai-thinking`,type:`number`,min:0,defaultValue:g?.thinking_budget_tokens??``,placeholder:`Disabled`,className:e?`h-7 text-[11px]`:void 0,onBlur:e=>{let t=parseInt(e.target.value);v(`thinking_budget_tokens`,isNaN(t)?void 0:t)}},`thinking-${r}-${p}`)]}),(0,I.jsxs)(`div`,{className:`flex items-center justify-between gap-2`,children:[(0,I.jsxs)(`div`,{children:[(0,I.jsx)(L,{htmlFor:`ai-agent-teams`,className:e?y:void 0,children:`Agent Teams`}),(0,I.jsx)(`p`,{className:`${e?`text-[9px]`:`text-[11px]`} text-muted-foreground`,children:`Experimental. Enables multi-agent collaboration with shared tasks and messaging. Uses ~7x more tokens.`})]}),(0,I.jsx)(R,{id:`ai-agent-teams`,checked:g?.agent_teams??!1,onCheckedChange:e=>v(`agent_teams`,e)})]}),g?.agent_teams&&(0,I.jsx)($,{compact:e})]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-permission-mode`,className:e?y:void 0,children:`Default Permission Mode`}),(0,I.jsxs)(z,{value:g?.permission_mode??`bypassPermissions`,onValueChange:e=>v(`permission_mode`,e),children:[(0,I.jsx)(V,{id:`ai-permission-mode`,className:`w-full ${e?`h-7 text-[11px]`:``}`,children:(0,I.jsx)(B,{})}),(0,I.jsx)(H,{children:X.map(e=>(0,I.jsx)(U,{value:e.value,children:e.label},e.value))})]})]}),(0,I.jsxs)(`div`,{className:w,children:[(0,I.jsx)(L,{htmlFor:`ai-system-prompt`,className:e?y:void 0,children:`Additional Instructions`}),(0,I.jsx)(`textarea`,{id:`ai-system-prompt`,rows:e?3:4,defaultValue:g?.system_prompt??``,placeholder:`Enter additional instructions for ${r}...`,className:`w-full rounded-md border border-input bg-background px-3 py-2 ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${e?`text-[11px]`:`text-sm`}`,onBlur:e=>{v(`system_prompt`,e.target.value.trim()||void 0)}},`sysprompt-${r}-${p}`)]})]}),l&&(0,I.jsx)(`p`,{className:`text-xs text-text-subtle`,children:`Saving...`}),d&&(0,I.jsx)(`p`,{className:`text-xs text-red-500`,children:d})]})}function $({compact:e}){let[t,n]=(0,F.useState)([]),[r,i]=(0,F.useState)(!1),[a,o]=(0,F.useState)(null),s=(0,F.useCallback)(async()=>{i(!0);try{n(await E.get(`/api/teams`)??[])}catch{}i(!1)},[]);(0,F.useEffect)(()=>{s()},[s]);let c=async e=>{try{await E.del(`/api/teams/${encodeURIComponent(e)}`),n(t=>t.filter(t=>t.name!==e)),o(null)}catch{}};return t.length===0&&!r?null:(0,I.jsxs)(`div`,{className:`space-y-1.5`,children:[(0,I.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,I.jsxs)(L,{className:e?`text-[11px]`:void 0,children:[`Teams (`,t.length,`)`]}),(0,I.jsx)(`button`,{onClick:s,className:`text-text-subtle hover:text-foreground p-1`,"aria-label":`Refresh teams`,children:(0,I.jsx)(C,{className:`size-3 ${r?`animate-spin`:``}`})})]}),t.map(e=>(0,I.jsxs)(`div`,{className:`flex items-center justify-between p-2 rounded bg-surface-elevated text-xs`,children:[(0,I.jsxs)(`div`,{className:`min-w-0`,children:[(0,I.jsx)(`div`,{className:`font-medium truncate`,children:e.name}),e.description&&(0,I.jsx)(`div`,{className:`text-text-subtle truncate`,children:e.description}),(0,I.jsxs)(`div`,{className:`text-text-subtle`,children:[e.members?.length??e.memberCount??0,` members`,e.createdAt?` · ${new Date(e.createdAt).toLocaleDateString()}`:``]})]}),a===e.name?(0,I.jsxs)(`div`,{className:`flex gap-1 shrink-0 ml-2`,children:[(0,I.jsx)(`button`,{onClick:()=>c(e.name),className:`px-2 py-1 bg-red-600 text-white rounded text-[10px]`,children:`Delete`}),(0,I.jsx)(`button`,{onClick:()=>o(null),className:`px-2 py-1 bg-zinc-600 text-white rounded text-[10px]`,children:`Cancel`})]}):(0,I.jsx)(`button`,{onClick:()=>o(e.name),className:`shrink-0 text-text-subtle hover:text-red-500 p-1 ml-2`,"aria-label":`Delete team ${e.name}`,children:(0,I.jsx)(w,{className:`size-3.5`})})]},e.name))]})}export{H as a,B as c,P as d,N as f,A as h,z as i,R as l,j as m,J as n,U as o,M as p,q as r,V as s,Q as t,L as u};
@@ -1 +0,0 @@
1
- import{W as e}from"./vendor-mermaid-CMiurk2b.js";export{e as createArchitectureServices};
@@ -1,8 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/markdown-renderer-CHWA0KAo.js","assets/rolldown-runtime-FhOqtrmT.js","assets/index-ZFyltHwi.js","assets/vendor-mermaid-CMiurk2b.js","assets/vendor-ui-B-89Uj8i.js","assets/vendor-markdown-0Mxgxy0L.js","assets/input-BMvRUOr7.js","assets/utils-CTg5uAYR.js","assets/createLucideIcon-BjHrJDVb.js","assets/x-BtqbfkR7.js","assets/settings-store-BHBb62gq.js","assets/react-GqWghJ-L.js","assets/api-client-Dvzcc_EO.js","assets/scroll-area-BEllam7_.js","assets/ai-settings-section-CHgpQ_OP.js","assets/dist-D7KGU7Vl.js","assets/plus-51UQ45rf.js","assets/refresh-cw-CSFrDtiu.js","assets/trash-2-CJYoLw7Q.js","assets/api-settings-D0_eiIYv.js","assets/chevron-right-BzAdxJRG.js","assets/database-DCT0OjgQ.js","assets/file-store-BrbCNyLm.js","assets/tab-store-0rGchMXr.js","assets/index-C1RBJe0a.css","assets/csv-preview-D5lmgVEy.js","assets/lib-D_kRA9p6.js","assets/arrow-up-Dtrfv490.js","assets/csv-parser-DO0dz4x_.js","assets/image-preview-CbFFD9BS.js","assets/file-exclamation-point-Baz81y5z.js","assets/use-blob-url-Hn6n1730.js","assets/pdf-preview-DQMdjqa2.js","assets/video-preview-BLI_RruT.js","assets/audio-preview-DQbX8gfL.js"])))=>i.map(i=>d[i]);
2
- import{o as e}from"./rolldown-runtime-FhOqtrmT.js";import{b as t,x as n}from"./vendor-markdown-0Mxgxy0L.js";import"./vendor-ui-B-89Uj8i.js";import{t as r}from"./createLucideIcon-BjHrJDVb.js";import"./scroll-area-BEllam7_.js";import{t as i}from"./chevron-right-BzAdxJRG.js";import{a,l as o,n as s,o as c,r as l,s as u,t as d}from"./input-BMvRUOr7.js";import{t as f}from"./code-CuravVys.js";import{t as ee}from"./database-DCT0OjgQ.js";import{n as p,r as m,t as h}from"./x-BtqbfkR7.js";import{t as g}from"./file-exclamation-point-Baz81y5z.js";import{t as _}from"./table-Dq575bPF.js";import{t as v}from"./text-wrap-Cn6BNQfq.js";import{i as y,t as b}from"./api-client-Dvzcc_EO.js";import{n as te}from"./settings-store-BHBb62gq.js";import{G as x}from"./vendor-mermaid-CMiurk2b.js";import{t as S}from"./utils-CTg5uAYR.js";import{n as ne,t as re}from"./tab-store-0rGchMXr.js";import{r as ie,t as C}from"./file-store-BrbCNyLm.js";import{G as w,J as T,Q as ae,X as E,Y as D,Z as O,a as oe,c as se,d as k,f as A,g as j,h as ce,j as M,l as N,m as P,nt as F,o as I,p as le,q as ue,u as L,z as de}from"./index-ZFyltHwi.js";import{n as fe,t as pe}from"./use-monaco-theme-CP-vyTF8.js";import{n as me,t as he}from"./sql-completion-provider-tCzZfqWs.js";var ge=r(`redo-2`,[[`path`,{d:`m15 14 5-5-5-5`,key:`12vg1m`}],[`path`,{d:`M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13`,key:`6uklza`}]]),R=e(n(),1),z=t(),_e={ts:O,tsx:O,js:O,jsx:O,py:O,rs:O,go:O,html:O,css:O,scss:O,json:ae,md:E,txt:E,yaml:D,yml:D};function ve(e,t){return t?ue:_e[e.split(`.`).pop()?.toLowerCase()??``]??T}function ye(e,t){let n=[],r=e;for(let e=0;e<t.length;e++){let i=t[e],a=t.slice(0,e+1).join(`/`),o=r.find(e=>e.name===i);if(n.push({name:i,fullPath:a,node:o??null,siblings:r}),o?.children)r=o.children;else{for(let r=e+1;r<t.length;r++)n.push({name:t[r],fullPath:t.slice(0,r+1).join(`/`),node:null,siblings:[]});break}}return n}function B(e){return[...e].sort((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1)}function be({filePath:e,projectName:t,tabId:n,className:r}){let a=C(e=>e.tree),{updateTab:o,openTab:s}=re(ce(e=>({updateTab:e.updateTab,openTab:e.openTab}))),c=(0,R.useRef)(null),l=(0,R.useMemo)(()=>ye(a,e.split(`/`).filter(Boolean)),[a,e]);(0,R.useEffect)(()=>{c.current&&(c.current.scrollLeft=c.current.scrollWidth)},[l]);function u(e,r){let i=S(e);r.metaKey||r.ctrlKey?s({type:`editor`,title:i,metadata:{filePath:e,projectName:t},projectId:t,closable:!0}):o(n,{title:i,metadata:{filePath:e,projectName:t}})}return(0,z.jsx)(`div`,{ref:c,className:r,children:l.map((e,n)=>(0,z.jsxs)(`div`,{className:`flex items-center shrink-0`,children:[n>0&&(0,z.jsx)(i,{className:`size-3 text-muted-foreground shrink-0 mx-0.5`}),e.siblings.length>0?(0,z.jsx)(xe,{segment:e,isLast:n===l.length-1,projectName:t,onFileClick:u}):(0,z.jsx)(`span`,{className:`text-xs text-muted-foreground px-1 py-0.5`,children:e.name})]},e.fullPath))})}function xe({segment:e,isLast:t,projectName:n,onFileClick:r}){let i=(0,R.useMemo)(()=>B(e.siblings),[e.siblings]);return(0,z.jsxs)(se,{children:[(0,z.jsx)(P,{asChild:!0,children:(0,z.jsx)(`button`,{type:`button`,className:`text-xs px-1 py-0.5 rounded hover:bg-muted transition-colors truncate max-w-[120px] ${t?`text-foreground font-medium`:`text-muted-foreground`}`,children:e.name})}),(0,z.jsx)(N,{align:`start`,className:`max-h-[300px] p-1`,children:i.map(t=>(0,z.jsx)(V,{node:t,projectName:n,activePath:e.fullPath,onFileClick:r},t.path))})]})}function V({node:e,projectName:t,activePath:n,onFileClick:r}){let i=ve(e.name,e.type===`directory`),a=e.path===n;return e.type===`directory`&&e.children&&e.children.length>0?(0,z.jsxs)(k,{children:[(0,z.jsxs)(le,{className:`text-xs gap-1.5 ${a?`bg-muted`:``}`,children:[(0,z.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,z.jsx)(`span`,{className:`truncate`,children:e.name})]}),(0,z.jsx)(A,{className:`max-h-[300px] overflow-y-auto p-1`,children:B(e.children).map(e=>(0,z.jsx)(V,{node:e,projectName:t,activePath:n,onFileClick:r},e.path))})]}):(0,z.jsxs)(L,{className:`text-xs gap-1.5 cursor-pointer ${a?`bg-muted`:``}`,onSelect:e=>{},onClick:t=>{e.type!==`directory`&&r(e.path,t)},children:[(0,z.jsx)(i,{className:`size-3.5 shrink-0 text-muted-foreground`}),(0,z.jsx)(`span`,{className:`truncate`,children:e.name})]})}function H({active:e,onClick:t,icon:n,label:r}){return(0,z.jsxs)(`button`,{type:`button`,onClick:t,className:`flex items-center gap-1 px-2 py-1 rounded text-xs transition-colors ${e?`bg-muted text-foreground`:`text-muted-foreground hover:text-foreground`}`,children:[(0,z.jsx)(n,{className:`size-3`}),(0,z.jsx)(`span`,{className:`hidden sm:inline`,children:r})]})}function Se({ext:e,mdMode:t,onMdModeChange:n,csvMode:r,onCsvModeChange:i,wordWrap:a,onToggleWordWrap:o,filePath:s,projectName:c,className:l}){return(0,z.jsxs)(`div`,{className:l,children:[(e===`md`||e===`mdx`)&&n&&(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(H,{active:t===`edit`,onClick:()=>n(`edit`),icon:f,label:`Edit`}),(0,z.jsx)(H,{active:t===`preview`,onClick:()=>n(`preview`),icon:p,label:`Preview`})]}),e===`csv`&&i&&(0,z.jsxs)(z.Fragment,{children:[(0,z.jsx)(H,{active:r===`table`,onClick:()=>i(`table`),icon:_,label:`Table`}),(0,z.jsx)(H,{active:r===`raw`,onClick:()=>i(`raw`),icon:f,label:`Raw`})]}),(0,z.jsx)(H,{active:a,onClick:o,icon:v,label:`Wrap`}),s&&c&&(0,z.jsx)(H,{active:!1,onClick:()=>j(c,s),icon:m,label:`Download`})]})}function Ce({open:e,defaultName:t,content:n,onSave:r,onCancel:i}){let[f,ee]=(0,R.useState)(t),[p,m]=(0,R.useState)(!1),[h,g]=(0,R.useState)(``),_=ie(e=>e.activeProject),v=(0,R.useCallback)(()=>{let e=f.trim();if(!e){g(`Filename cannot be empty`);return}if(/[/\\]/.test(e)){g(`Filename cannot contain / or \\`);return}g(``),m(!0)},[f]),y=(0,R.useCallback)(e=>{let t=e.includes(`\\`)?`\\`:`/`;r(e.endsWith(t)?`${e}${f.trim()}`:`${e}${t}${f.trim()}`,n)},[f,n,r]);return p?(0,z.jsx)(I,{open:!0,mode:`folder`,root:_?.path,title:`Save "${f.trim()}" to...`,onSelect:y,onCancel:()=>m(!1)}):(0,z.jsx)(s,{open:e,onOpenChange:e=>{e||i()},children:(0,z.jsxs)(l,{className:`sm:max-w-md`,children:[(0,z.jsx)(c,{children:(0,z.jsx)(u,{children:`Save As`})}),(0,z.jsxs)(`div`,{className:`flex flex-col gap-2 py-2`,children:[(0,z.jsx)(`label`,{className:`text-sm text-muted-foreground`,children:`Filename`}),(0,z.jsx)(d,{value:f,onChange:e=>{ee(e.target.value),g(``)},onKeyDown:e=>{e.key===`Enter`&&v()},placeholder:`e.g. my-file.ts`,autoFocus:!0}),h&&(0,z.jsx)(`p`,{className:`text-xs text-destructive`,children:h})]}),(0,z.jsxs)(a,{children:[(0,z.jsx)(o,{variant:`outline`,onClick:i,children:`Cancel`}),(0,z.jsx)(o,{onClick:v,children:`Choose Folder...`})]})]})})}var we=typeof window<`u`&&window.isSecureContext,U=[`(`,`)`,`{`,`}`,`[`,`]`,`<`,`>`,`;`,`:`,`=`,`"`,`'`,"`",`/`,`\\`,`_`,`#`],W=`px-2 py-1.5 rounded text-xs min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none`,G=`px-3 py-1.5 rounded text-xs font-mono min-w-[36px] min-h-[32px] bg-surface-elevated text-text-primary active:bg-primary active:text-primary-foreground transition-colors select-none`,Te=`w-px h-5 bg-border mx-0.5 shrink-0`;function Ee({editorRef:e,readOnly:t}){let n=(0,R.useCallback)(()=>e.current,[e]),r=(0,R.useCallback)(e=>{let t=n();if(!t)return;t.focus();let r=t.getSelection();r&&t.executeEdits(`mobile-toolbar`,[{range:r,text:e}])},[n]),[i,a]=(0,R.useState)(!1),o=(0,R.useRef)(null),s=(0,R.useCallback)(async()=>{try{let e=await navigator.clipboard.readText();e&&r(e)}catch{}},[r]),c=(0,R.useCallback)(()=>{a(!0),requestAnimationFrame(()=>o.current?.focus())},[]),l=(0,R.useCallback)(e=>{e.preventDefault();let t=e.clipboardData.getData(`text/plain`);t&&(a(!1),r(t))},[r]),u=(0,R.useCallback)(()=>{let e=n();e&&(e.focus(),e.trigger(`mobile-toolbar`,`undo`,null))},[n]),d=(0,R.useCallback)(()=>{let e=n();e&&(e.focus(),e.trigger(`mobile-toolbar`,`redo`,null))},[n]),f=(0,R.useCallback)(()=>{let e=n();e&&(e.focus(),e.trigger(`mobile-toolbar`,`tab`,null))},[n]);return t?null:(0,z.jsxs)(`div`,{className:`shrink-0 border-t border-border bg-surface`,children:[!we&&i&&(0,z.jsxs)(`div`,{className:`flex items-center gap-2 px-2 py-1.5 border-b border-border bg-muted/50`,children:[(0,z.jsx)(`textarea`,{ref:o,onPaste:l,placeholder:`Long-press here → Paste`,className:`flex-1 h-8 rounded border border-border bg-background text-foreground text-xs px-2 py-1.5 resize-none focus:outline-none focus:ring-1 focus:ring-primary`}),(0,z.jsx)(`button`,{type:`button`,onClick:()=>a(!1),className:`p-1.5 rounded text-muted-foreground active:bg-muted transition-colors`,children:(0,z.jsx)(h,{size:14})})]}),(0,z.jsxs)(`div`,{className:`flex items-center gap-1 px-2 py-1.5 overflow-x-auto`,children:[(0,z.jsx)(`button`,{type:`button`,onClick:we?s:c,className:W,title:`Paste`,children:(0,z.jsx)(F,{size:14})}),(0,z.jsx)(`button`,{type:`button`,onClick:u,className:W,title:`Undo`,children:(0,z.jsx)(M,{size:14})}),(0,z.jsx)(`button`,{type:`button`,onClick:d,className:W,title:`Redo`,children:(0,z.jsx)(ge,{size:14})}),(0,z.jsx)(`div`,{className:Te}),(0,z.jsx)(`button`,{type:`button`,onClick:f,className:G,children:`Tab`}),(0,z.jsx)(`div`,{className:Te}),U.map(e=>(0,z.jsx)(`button`,{type:`button`,onClick:()=>r(e),className:G,children:e},e))]})]})}var K=(0,R.lazy)(()=>x(()=>import(`./markdown-renderer-CHWA0KAo.js`).then(e=>({default:e.MarkdownRenderer})),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24]))),De=(0,R.lazy)(()=>x(()=>import(`./csv-preview-D5lmgVEy.js`).then(e=>({default:e.CsvPreview})),__vite__mapDeps([25,1,4,5,26,8,27,28]))),Oe=(0,R.lazy)(()=>x(()=>import(`./image-preview-CbFFD9BS.js`).then(e=>({default:e.ImagePreview})),__vite__mapDeps([29,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),ke=(0,R.lazy)(()=>x(()=>import(`./pdf-preview-DQMdjqa2.js`).then(e=>({default:e.PdfPreview})),__vite__mapDeps([32,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),Ae=(0,R.lazy)(()=>x(()=>import(`./video-preview-BLI_RruT.js`).then(e=>({default:e.VideoPreview})),__vite__mapDeps([33,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),je=(0,R.lazy)(()=>x(()=>import(`./audio-preview-DQbX8gfL.js`).then(e=>({default:e.AudioPreview})),__vite__mapDeps([34,2,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,30,31]))),Me=new Set([`png`,`jpg`,`jpeg`,`gif`,`webp`,`svg`,`ico`]),Ne=new Set([`mp4`,`webm`,`mov`,`ogg`,`avi`,`mkv`]),Pe=new Set([`mp3`,`wav`,`flac`,`aac`,`m4a`,`wma`]),Fe=new Set([`db`,`sqlite`,`sqlite3`]);function Ie(e){return e.split(`.`).pop()?.toLowerCase()??``}function Le(e){return{js:`javascript`,jsx:`javascript`,ts:`typescript`,tsx:`typescript`,py:`python`,html:`html`,css:`css`,scss:`scss`,json:`json`,md:`markdown`,mdx:`markdown`,yaml:`yaml`,yml:`yaml`,sh:`shell`,bash:`shell`,sql:`sql`}[Ie(e)]??`plaintext`}var q=(0,R.memo)(function({metadata:e,tabId:t}){let n=e?.filePath,r=e?.projectName,i=e?.inlineContent,a=e?.inlineLanguage,[o,s]=(0,R.useState)(i??null),[c,l]=(0,R.useState)(`utf-8`),[u,d]=(0,R.useState)(!0),[f,p]=(0,R.useState)(null),[m,h]=(0,R.useState)(!1),_=(0,R.useRef)(null),v=(0,R.useRef)(``),x=(0,R.useRef)(null),{tabs:ie,updateTab:C}=re(ce(e=>({tabs:e.tabs,updateTab:e.updateTab}))),{wordWrap:T,toggleWordWrap:ae}=te(ce(e=>({wordWrap:e.wordWrap,toggleWordWrap:e.toggleWordWrap}))),E=pe(),D=e?.isUntitled===!0,O=e?.unsavedContent,[se,k]=(0,R.useState)(!1),A=ie.find(e=>e.id===t),j=n?Ie(n):``,M=Me.has(j),N=j===`pdf`,P=Ne.has(j),F=Pe.has(j),I=Fe.has(j),le=j===`md`||j===`mdx`,ue=j===`csv`,L=j===`sql`,[ge,_e]=(0,R.useState)(`preview`),[ve,ye]=(0,R.useState)(`table`),{connections:B,cachedTables:xe,refreshTables:V}=oe(),[H,we]=(0,R.useState)(()=>{if(!L||!n)return null;let e=localStorage.getItem(`ppm:sql-conn:${n}`);return e?Number(e):null}),U=(0,R.useRef)(null),W=(0,R.useRef)(null),G=(0,R.useMemo)(()=>B.find(e=>e.id===H)??null,[B,H]),Te=i!=null&&(a===`json`||a===`xml`),[K,q]=(0,R.useState)(!1),ze=(0,R.useCallback)(()=>{if(i)if(K)s(i),q(!1);else{let e=i.trimStart();if(a===`json`)try{s(JSON.stringify(JSON.parse(e),null,2)),q(!0)}catch{}else if(a===`xml`){let t=0;s(e.replace(/(>)(<)(\/*)/g,`$1
3
- $2$3`).split(`
4
- `).map(e=>{let n=e.trim();n.startsWith(`</`)&&(t=Math.max(0,t-1));let r=` `.repeat(t)+n;return n.startsWith(`<`)&&!n.startsWith(`</`)&&!n.endsWith(`/>`)&&!n.includes(`</`)&&t++,r}).join(`
5
- `)),q(!0)}}},[i,a,K]),Be=(0,R.useCallback)(e=>{we(e),n&&localStorage.setItem(`ppm:sql-conn:${n}`,String(e)),V(e).catch(()=>{})},[n,V]),Y=(0,R.useMemo)(()=>{if(!L||!H)return;let e=(xe.get(H)??[]).map(e=>({name:e.tableName,schema:e.schemaName}));if(e.length!==0)return{tables:e,getColumns:async(e,t)=>b.get(`/api/db/connections/${H}/schema?table=${encodeURIComponent(e)}${t?`&schema=${encodeURIComponent(t)}`:``}`)}},[L,H,xe]);(0,R.useEffect)(()=>{if(!(!U.current||!Y))return W.current?.dispose(),he(),W.current=U.current.languages.registerCompletionItemProvider(`sql`,me(U.current,Y)),()=>{W.current?.dispose()}},[Y]);let Ve=re(e=>e.openTab),X=(0,R.useCallback)(e=>{G&&Ve({type:`database`,title:`${G.name} · Query`,projectId:null,closable:!0,metadata:{connectionId:G.id,connectionName:G.name,dbType:G.type,initialSql:e}})},[G,Ve]),He=(0,R.useCallback)(()=>{if(!x.current||!G)return;let e=x.current,t=e.getSelection();X(t&&!t.isEmpty()?e.getModel()?.getValueInRange(t)??e.getValue():e.getValue())},[G,X]),Ue=typeof window<`u`&&`ontouchstart`in window,We=(0,R.useRef)(null),[Ge,Ke]=(0,R.useState)(null);(0,R.useEffect)(()=>{if(!Ue)return;let e=window.visualViewport;if(!e)return;let t=()=>{let t=We.current;if(!t)return;let n=t.getBoundingClientRect().top;Ke(e.height-Math.max(0,n))};return e.addEventListener(`resize`,t),e.addEventListener(`scroll`,t),()=>{e.removeEventListener(`resize`,t),e.removeEventListener(`scroll`,t)}},[Ue]);let Z=(0,R.useRef)([]),qe=(0,R.useRef)(X);qe.current=X,(0,R.useEffect)(()=>()=>{Z.current.forEach(e=>e.dispose()),Z.current=[]},[]),(0,R.useEffect)(()=>{I&&t&&C(t,{type:`sqlite`})},[I,t,C]);let Q=n?/^(\/|[A-Za-z]:[/\\])/.test(n):!1;(0,R.useEffect)(()=>{if(i!=null){d(!1);return}if(D){s(O??``),v.current=O??``,d(!1),O&&h(!0);return}if(!n||!Q&&!r)return;if(M||N||P||F){d(!1);return}d(!0),p(null);let e=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${y(r)}/files/read?path=${encodeURIComponent(n)}`;return b.get(e).then(e=>{s(e.content),e.encoding&&l(e.encoding),v.current=e.content,d(!1)}).catch(e=>{p(e instanceof Error?e.message:`Failed to load file`),d(!1)}),()=>{_.current&&clearTimeout(_.current)}},[n,r,M,N,Q,D]);let Je=(0,R.useRef)(m);Je.current=m,(0,R.useEffect)(()=>{if(!n||!r||i!=null||D)return;let e=e=>{let t=e.detail;if(t.projectName!==r||t.path!==n||Je.current)return;let i=Q?`/api/fs/read?path=${encodeURIComponent(n)}`:`${y(r)}/files/read?path=${encodeURIComponent(n)}`;b.get(i).then(e=>{e.content!==v.current&&(s(e.content),v.current=e.content,e.encoding&&l(e.encoding))}).catch(()=>{})};return window.addEventListener(`file:changed`,e),()=>window.removeEventListener(`file:changed`,e)},[n,r,Q,i,D]),(0,R.useEffect)(()=>{if(!A||i!=null)return;let t=D?`Untitled-${e?.untitledNumber??1}`:n?S(n):`Untitled`,r=m?`${t} \u25CF`:t;A.title!==r&&C(A.id,{title:r})},[m]);let Ye=(0,R.useCallback)(async e=>{if(n&&!(!Q&&!r))try{Q?await b.put(`/api/fs/write`,{path:n,content:e}):await b.put(`${y(r)}/files/write`,{path:n,content:e}),h(!1)}catch{}},[n,r,Q]);function Xe(n){let r=n??``;s(r),v.current=r,h(!0),_.current&&clearTimeout(_.current),D?_.current=setTimeout(()=>{t&&C(t,{metadata:{...e,unsavedContent:v.current}})},2e3):_.current=setTimeout(()=>Ye(v.current),1e3)}let Ze=(0,R.useCallback)(async(e,n)=>{try{if(_.current&&clearTimeout(_.current),await b.put(`/api/fs/write`,{path:e,content:n}),t){let{closeTab:n,openTab:r}=ne.getState();n(t),r({type:`editor`,title:S(e),projectId:null,metadata:{filePath:e},closable:!0})}h(!1),k(!1)}catch{}},[t]),$=e?.lineNumber,Qe=(0,R.useCallback)((e,t)=>{if(x.current=e,U.current=t,$&&$>0&&setTimeout(()=>{e.revealLineInCenter($),e.setPosition({lineNumber:$,column:1}),e.focus()},100),D&&e.addCommand(t.KeyMod.CtrlCmd|t.KeyCode.KeyS,()=>k(!0)),e.addCommand(t.KeyMod.Alt|t.KeyCode.KeyZ,()=>te.getState().toggleWordWrap()),t.languages.typescript.typescriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),t.languages.typescript.javascriptDefaults.setDiagnosticsOptions({noSemanticValidation:!0,noSyntaxValidation:!0,noSuggestionDiagnostics:!0}),Y&&(W.current?.dispose(),W.current=t.languages.registerCompletionItemProvider(`sql`,me(t,Y))),L){Z.current.forEach(e=>e.dispose()),Z.current=[];let n=e.getModel(),r=e.addCommand(0,(e,t)=>{t&&qe.current(t)});if(r&&n){let e=t.languages.registerCodeLensProvider(`sql`,{provideCodeLenses:e=>{if(e!==n)return{lenses:[],dispose:()=>{}};let t=[],i=e.getValue().split(`
6
- `),a=-1,o=[],s=!1,c=(e,n)=>{let i=n.trim();!i||i.startsWith(`--`)||t.push({range:{startLineNumber:e,startColumn:1,endLineNumber:e,endColumn:1},command:{id:r,title:`▷ Run`,arguments:[i]}})};for(let e=0;e<i.length;e++){let t=i[e].trim();if(a===-1){if(!t||t.startsWith(`--`))continue;a=e+1,o=[]}o.push(i[e]),(t.match(/\$\$/g)||[]).length%2==1&&(s=!s),!s&&t.endsWith(`;`)&&(c(a,o.join(`
7
- `)),a=-1,o=[])}return a>0&&o.join(``).trim()&&c(a,o.join(`
8
- `)),{lenses:t,dispose:()=>{}}}});Z.current.push(e)}}},[Y]);if(!i&&!D&&(!n||!Q&&!r))return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-text-secondary text-sm`,children:`No file selected.`});if(u)return(0,z.jsxs)(`div`,{className:`flex items-center justify-center h-full gap-2 text-text-secondary`,children:[(0,z.jsx)(w,{className:`size-5 animate-spin`}),(0,z.jsx)(`span`,{className:`text-sm`,children:`Loading file...`})]});if(f)return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full text-error text-sm`,children:f});if(M)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(J,{}),children:(0,z.jsx)(Oe,{filePath:n,projectName:r})});if(N)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(J,{}),children:(0,z.jsx)(ke,{filePath:n,projectName:r})});if(P)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(J,{}),children:(0,z.jsx)(Ae,{filePath:n,projectName:r})});if(F)return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(J,{}),children:(0,z.jsx)(je,{filePath:n,projectName:r})});if(c===`base64`)return(0,z.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-full gap-3 text-text-secondary`,children:[(0,z.jsx)(g,{className:`size-10 text-text-subtle`}),(0,z.jsx)(`p`,{className:`text-sm`,children:`This file is a binary format and cannot be displayed.`}),(0,z.jsx)(`p`,{className:`text-xs text-text-subtle`,children:n})]});let $e=L?(0,z.jsxs)(`div`,{className:`shrink-0 flex items-center gap-1 px-2 border-l border-border`,children:[(0,z.jsx)(ee,{className:`size-3 text-muted-foreground`}),(0,z.jsxs)(`select`,{value:H??``,onChange:e=>{let t=Number(e.target.value);t&&Be(t)},className:`h-5 text-[10px] bg-transparent border border-border rounded px-1 text-foreground outline-none max-w-[140px]`,title:`Select connection for autocomplete`,children:[(0,z.jsx)(`option`,{value:``,children:`Connection…`}),B.map(e=>(0,z.jsx)(`option`,{value:e.id,children:e.name},e.id))]}),(0,z.jsx)(`button`,{type:`button`,onClick:He,disabled:!G,className:`p-0.5 rounded text-muted-foreground hover:text-primary disabled:opacity-30 transition-colors`,title:`Run all in DB Viewer`,children:(0,z.jsx)(de,{className:`size-3.5`})})]}):null;return(0,z.jsxs)(`div`,{ref:We,className:`flex flex-col h-full w-full overflow-hidden`,style:Ge?{height:`${Ge}px`,maxHeight:`${Ge}px`}:void 0,children:[i!=null&&Te&&(0,z.jsx)(`div`,{className:`flex items-center h-7 border-b border-border bg-background shrink-0 px-2 gap-2`,children:(0,z.jsx)(`button`,{type:`button`,onClick:ze,className:`text-[10px] px-2 py-0.5 rounded border border-border hover:bg-muted transition-colors text-foreground`,children:K?`Raw`:`Beautify`})}),n&&r&&t&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0`,children:[(0,z.jsx)(be,{filePath:n,projectName:r,tabId:t,className:`flex items-center flex-1 min-w-0 overflow-x-auto scrollbar-none px-2 gap-0.5`}),$e,(0,z.jsx)(Se,{ext:j,mdMode:ge,onMdModeChange:_e,csvMode:ve,onCsvModeChange:ye,wordWrap:T,onToggleWordWrap:ae,filePath:n,projectName:r,className:`shrink-0 flex items-center gap-1 px-2`})]}),L&&(!r||!t)&&(0,z.jsxs)(`div`,{className:`hidden md:flex items-center h-7 border-b border-border bg-background shrink-0 px-2`,children:[(0,z.jsx)(`span`,{className:`text-xs text-muted-foreground truncate flex-1`,children:n?S(n):`SQL`}),$e]}),ue&&ve===`table`?(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})}),children:(0,z.jsx)(De,{content:o??``,onContentChange:Xe,wordWrap:T})}):le&&ge===`preview`?(0,z.jsx)(Re,{content:o??``}):(0,z.jsx)(`div`,{className:`flex-1 overflow-hidden`,children:(0,z.jsx)(fe,{height:`100%`,language:a??Le(n??``),value:o??``,onChange:i==null?Xe:void 0,onMount:Qe,theme:E,options:{fontSize:13,fontFamily:`Menlo, Monaco, Consolas, monospace`,wordWrap:T?`on`:`off`,minimap:{enabled:!1},scrollBeyondLastLine:!1,automaticLayout:!0,lineNumbers:`on`,folding:!0,bracketPairColorization:{enabled:!0},readOnly:i!=null},loading:(0,z.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})})}),Ue&&(0,z.jsx)(Ee,{editorRef:x,readOnly:i!=null}),se&&(0,z.jsx)(Ce,{open:se,defaultName:`Untitled-${e?.untitledNumber??1}`,content:v.current,onSave:Ze,onCancel:()=>k(!1)})]})});function J(){return(0,z.jsx)(`div`,{className:`flex items-center justify-center h-full`,children:(0,z.jsx)(w,{className:`size-5 animate-spin text-text-subtle`})})}function Re({content:e}){return(0,z.jsx)(R.Suspense,{fallback:(0,z.jsx)(`div`,{className:`animate-pulse h-4 bg-muted rounded m-4`}),children:(0,z.jsx)(K,{content:e,className:`flex-1 overflow-auto p-4`})})}export{q as CodeEditor};
@@ -1,6 +0,0 @@
1
- var e=function(e){return e[e.FIELD_START=0]=`FIELD_START`,e[e.UNQUOTED=1]=`UNQUOTED`,e[e.QUOTED=2]=`QUOTED`,e[e.QUOTE_IN_QUOTED=3]=`QUOTE_IN_QUOTED`,e}(e||{});function t(t){let n=[],r=[],i=``,a=e.FIELD_START;for(let o=0;o<t.length;o++){let s=t[o];switch(a){case e.FIELD_START:s===`"`?a=e.QUOTED:s===`,`?(r.push(i),i=``):s===`\r`||(s===`
2
- `?(r.push(i),i=``,n.push(r),r=[]):(i+=s,a=e.UNQUOTED));break;case e.UNQUOTED:s===`,`?(r.push(i),i=``,a=e.FIELD_START):s===`\r`||(s===`
3
- `?(r.push(i),i=``,n.push(r),r=[],a=e.FIELD_START):i+=s);break;case e.QUOTED:s===`"`?a=e.QUOTE_IN_QUOTED:i+=s;break;case e.QUOTE_IN_QUOTED:s===`"`?(i+=`"`,a=e.QUOTED):s===`,`?(r.push(i),i=``,a=e.FIELD_START):s===`\r`||(s===`
4
- `?(r.push(i),i=``,n.push(r),r=[],a=e.FIELD_START):(i+=s,a=e.UNQUOTED));break}}if((i||r.length>0)&&(r.push(i),n.push(r)),n.length===0)return{headers:[],rows:[]};let o=n[0],s=n.slice(1),c=o.length;for(let e=0;e<s.length;e++){let t=s[e];if(t.length<c)for(;t.length<c;)t.push(``);else t.length>c&&(s[e]=t.slice(0,c))}return{headers:o,rows:s}}function n(e,t){let n=e=>e.includes(`,`)||e.includes(`"`)||e.includes(`
5
- `)?`"${e.replace(/"/g,`""`)}"`:e,r=[e.map(n).join(`,`)];for(let e of t)r.push(e.map(n).join(`,`));return r.join(`
6
- `)}export{n,t};
@@ -1 +0,0 @@
1
- import{t as e}from"./createLucideIcon-BjHrJDVb.js";var t=e(`database`,[[`ellipse`,{cx:`12`,cy:`5`,rx:`9`,ry:`3`,key:`msslwz`}],[`path`,{d:`M3 5V19A9 3 0 0 0 21 19V5`,key:`1wlel7`}],[`path`,{d:`M3 12A9 3 0 0 0 21 12`,key:`mv7ke4`}]]);export{t};