@hienlh/ppm 0.17.24 → 0.17.26

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 (45) hide show
  1. package/CHANGELOG.md +11 -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-resource-editor-CwZAAlN5.js → ai-resource-editor-qnr1gXKX.js} +1 -1
  5. package/dist/web/assets/{audio-preview-C8ePPqYM.js → audio-preview-IJyTkZhv.js} +1 -1
  6. package/dist/web/assets/{chat-tab-ByfGBWbW.js → chat-tab-Bf7bP_mH.js} +2 -2
  7. package/dist/web/assets/{code-editor-BLUWMpZn.js → code-editor-DbgRk7IQ.js} +2 -2
  8. package/dist/web/assets/{conflict-editor-B1oxNu29.js → conflict-editor-7379QQTo.js} +1 -1
  9. package/dist/web/assets/{csv-preview-CrGymnn9.js → csv-preview-CkZtgJJd.js} +1 -1
  10. package/dist/web/assets/{database-viewer-BTofJwov.js → database-viewer-BOWXg1eU.js} +1 -1
  11. package/dist/web/assets/{diff-viewer-Bsp25uLy.js → diff-viewer-3q8WtMhX.js} +1 -1
  12. package/dist/web/assets/{docx-preview-BULrDLso.js → docx-preview-uAcT8IrY.js} +1 -1
  13. package/dist/web/assets/{extension-webview-gByeDwuJ.js → extension-webview-C9B286ef.js} +1 -1
  14. package/dist/web/assets/{git-log-panel-BjB3uGkM.js → git-log-panel-CWfaQmIt.js} +1 -1
  15. package/dist/web/assets/{glide-data-grid-BmofPEFh.js → glide-data-grid-BTbN5x8y.js} +1 -1
  16. package/dist/web/assets/{group-chat-tab-DczxC8y0.js → group-chat-tab-CD1tHFCt.js} +1 -1
  17. package/dist/web/assets/{image-preview-C2JNJTTR.js → image-preview-D35tUSfv.js} +1 -1
  18. package/dist/web/assets/index-DMEso_fH.js +50 -0
  19. package/dist/web/assets/{keybindings-store-DWwuOAWb.js → keybindings-store-C56hagRp.js} +1 -1
  20. package/dist/web/assets/{markdown-renderer-BGumVkCU.js → markdown-renderer-CaVoLF6f.js} +1 -1
  21. package/dist/web/assets/{markdown-renderer-CvQmV_TU.js → markdown-renderer-w7cIJc1V.js} +1 -1
  22. package/dist/web/assets/{notification-store-OaYhYp1C.js → notification-store-CijmEBze.js} +1 -1
  23. package/dist/web/assets/{pdf-preview-DjWTE3jF.js → pdf-preview-URqY4tEa.js} +1 -1
  24. package/dist/web/assets/{postgres-viewer-K6ZyFyso.js → postgres-viewer-D3iSQpJq.js} +1 -1
  25. package/dist/web/assets/settings-store-UwT_fNhg.js +2 -0
  26. package/dist/web/assets/{settings-tab-DcUZ7ln3.js → settings-tab-BMAlaxBD.js} +1 -1
  27. package/dist/web/assets/{sql-query-editor-D9pM_Dys.js → sql-query-editor-Qrom7wG2.js} +1 -1
  28. package/dist/web/assets/{sqlite-viewer-zudsiPxP.js → sqlite-viewer-CPzA2RA4.js} +1 -1
  29. package/dist/web/assets/{system-monitor-tab-C7VZxGbH.js → system-monitor-tab-DEXXqZIz.js} +1 -1
  30. package/dist/web/assets/{terminal-tab-xiwZMQLE.js → terminal-tab-Cn1szPMh.js} +1 -1
  31. package/dist/web/assets/{tool-cards-C4DU4njU.js → tool-cards-DNftJX5Y.js} +2 -2
  32. package/dist/web/assets/{use-monaco-theme-D6Z2faTR.js → use-monaco-theme-XO5ZyYBW.js} +1 -1
  33. package/dist/web/assets/{video-preview-BL-sWnxi.js → video-preview-U2aMrCxv.js} +1 -1
  34. package/dist/web/index.html +2 -2
  35. package/dist/web/sw.js +1 -1
  36. package/package.json +1 -1
  37. package/src/cli/commands/status.ts +21 -2
  38. package/src/server/routes/settings.ts +11 -0
  39. package/src/services/autostart-generator.ts +5 -0
  40. package/src/services/supervisor.ts +61 -28
  41. package/src/web/components/database/connection-list.tsx +50 -28
  42. package/src/web/components/database/use-db-tree-expansion.ts +74 -0
  43. package/src/web/stores/settings-store.ts +43 -0
  44. package/dist/web/assets/index-D6qn4rtk.js +0 -50
  45. package/dist/web/assets/settings-store-C7VeYnhf.js +0 -2
@@ -0,0 +1,74 @@
1
+ import { useMemo, useCallback } from "react";
2
+ import { useSettingsStore, type DbSidebarExpanded } from "@/stores/settings-store";
3
+
4
+ /** Table nodes are keyed `${connId}:${schema}.${table}`. */
5
+ export function tableKey(connId: number, schemaName: string, tableName: string): string {
6
+ return `${connId}:${schemaName}.${tableName}`;
7
+ }
8
+
9
+ function connIdOfTableKey(key: string): number {
10
+ return Number(key.slice(0, key.indexOf(":")));
11
+ }
12
+
13
+ /**
14
+ * Expansion state of the Database sidebar tree, backed by the server-synced
15
+ * `dbSidebarExpanded` UI pref so it survives reloads, sidebar tab switches,
16
+ * and origin changes (new tunnel URL).
17
+ */
18
+ export function useDbTreeExpansion() {
19
+ const expanded = useSettingsStore((s) => s.dbSidebarExpanded);
20
+ const setExpanded = useSettingsStore((s) => s.setDbSidebarExpanded);
21
+
22
+ const expandedConns = useMemo(() => new Set(expanded.conns), [expanded.conns]);
23
+ const expandedGroups = useMemo(() => new Set(expanded.groups), [expanded.groups]);
24
+ const expandedTables = useMemo(() => new Set(expanded.tables), [expanded.tables]);
25
+
26
+ // Read through getState() so rapid successive toggles never write a stale set.
27
+ const update = useCallback(
28
+ (fn: (current: DbSidebarExpanded) => DbSidebarExpanded) => {
29
+ const current = useSettingsStore.getState().dbSidebarExpanded;
30
+ const next = fn(current);
31
+ // Returning `current` means "no change" — skipping the write keeps effects
32
+ // that call this (pruning) from re-triggering themselves via a new identity.
33
+ if (next === current) return;
34
+ setExpanded(next);
35
+ },
36
+ [setExpanded],
37
+ );
38
+
39
+ const toggleConn = useCallback((id: number) => {
40
+ update((cur) => ({
41
+ ...cur,
42
+ conns: cur.conns.includes(id) ? cur.conns.filter((c) => c !== id) : [...cur.conns, id],
43
+ }));
44
+ }, [update]);
45
+
46
+ const toggleGroup = useCallback((group: string) => {
47
+ update((cur) => ({
48
+ ...cur,
49
+ groups: cur.groups.includes(group) ? cur.groups.filter((g) => g !== group) : [...cur.groups, group],
50
+ }));
51
+ }, [update]);
52
+
53
+ const setTableExpanded = useCallback((key: string, isExpanded: boolean) => {
54
+ update((cur) => ({
55
+ ...cur,
56
+ tables: isExpanded
57
+ ? (cur.tables.includes(key) ? cur.tables : [...cur.tables, key])
58
+ : cur.tables.filter((t) => t !== key),
59
+ }));
60
+ }, [update]);
61
+
62
+ /** Drop entries for connections that no longer exist so the pref cannot grow forever. */
63
+ const pruneDeletedConns = useCallback((existingIds: number[]) => {
64
+ const alive = new Set(existingIds);
65
+ update((cur) => {
66
+ const conns = cur.conns.filter((id) => alive.has(id));
67
+ const tables = cur.tables.filter((k) => alive.has(connIdOfTableKey(k)));
68
+ if (conns.length === cur.conns.length && tables.length === cur.tables.length) return cur;
69
+ return { ...cur, conns, tables };
70
+ });
71
+ }, [update]);
72
+
73
+ return { expandedConns, expandedGroups, expandedTables, toggleConn, toggleGroup, setTableExpanded, pruneDeletedConns };
74
+ }
@@ -8,6 +8,13 @@ export type EditorTabStyle = "default" | "boxed" | "pill";
8
8
  export type DockPosition = "left" | "bottom" | "right";
9
9
  export type SidebarActiveTab = "explorer" | "git" | "settings" | "database" | "search" | "jira" | "ai-resources" | "history" | "tunnels" | "teams" | `ext:${string}`;
10
10
 
11
+ /** Expanded nodes of the Database sidebar tree. Table keys are `${connId}:${schema}.${table}`. */
12
+ export interface DbSidebarExpanded {
13
+ conns: number[];
14
+ groups: string[];
15
+ tables: string[];
16
+ }
17
+
11
18
  const STORAGE_KEY = "ppm-settings";
12
19
 
13
20
  interface SettingsState {
@@ -28,6 +35,7 @@ interface SettingsState {
28
35
  sidebarTabOrder: SidebarActiveTab[];
29
36
  jiraEnabled: boolean;
30
37
  dockPosition: DockPosition;
38
+ dbSidebarExpanded: DbSidebarExpanded;
31
39
  deviceName: string | null;
32
40
  version: string | null;
33
41
  tunnelActive: boolean;
@@ -50,6 +58,7 @@ interface SettingsState {
50
58
  setSidebarActiveTab: (tab: SidebarActiveTab) => void;
51
59
  setSidebarTabOrder: (order: SidebarActiveTab[]) => void;
52
60
  setDockPosition: (position: DockPosition) => void;
61
+ setDbSidebarExpanded: (next: DbSidebarExpanded) => void;
53
62
  fetchServerInfo: () => Promise<void>;
54
63
  }
55
64
 
@@ -69,6 +78,7 @@ interface PersistedSettings {
69
78
  sidebarTabOrder?: SidebarActiveTab[];
70
79
  jiraEnabled?: boolean;
71
80
  dockPosition?: DockPosition;
81
+ dbSidebarExpanded?: DbSidebarExpanded;
72
82
  }
73
83
 
74
84
  const VALID_STYLES: PpmThemeStyle[] = ["aurora", "slate", "precision", "custom"];
@@ -115,6 +125,30 @@ function sanitizeTabOrder(value: unknown): SidebarActiveTab[] {
115
125
  return out;
116
126
  }
117
127
 
128
+ // "__ungrouped__" is the synthetic group holding connections without a group.
129
+ const DEFAULT_DB_EXPANDED: DbSidebarExpanded = { conns: [], groups: ["__ungrouped__"], tables: [] };
130
+
131
+ // Caps keep the pref small enough to ride along with every ui-prefs write.
132
+ // Server mirrors these limits in its validator.
133
+ const DB_EXPANDED_CAPS = { conns: 200, groups: 200, tables: 500 } as const;
134
+
135
+ /** Coerce stored/server data into a valid expansion set; null when unusable. */
136
+ function sanitizeDbExpanded(value: unknown): DbSidebarExpanded | null {
137
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
138
+ const v = value as Record<string, unknown>;
139
+ const strings = (raw: unknown, cap: number) =>
140
+ Array.isArray(raw)
141
+ ? raw.filter((x): x is string => typeof x === "string" && x.length > 0 && x.length <= 300).slice(0, cap)
142
+ : [];
143
+ return {
144
+ conns: Array.isArray(v.conns)
145
+ ? v.conns.filter((x): x is number => typeof x === "number" && Number.isInteger(x)).slice(0, DB_EXPANDED_CAPS.conns)
146
+ : [],
147
+ groups: strings(v.groups, DB_EXPANDED_CAPS.groups),
148
+ tables: strings(v.tables, DB_EXPANDED_CAPS.tables),
149
+ };
150
+ }
151
+
118
152
  function persistSettings(update: Partial<PersistedSettings>) {
119
153
  const current = loadPersistedSettings();
120
154
  localStorage.setItem(STORAGE_KEY, JSON.stringify({ ...current, ...update }));
@@ -182,6 +216,8 @@ function applyServerUiPrefs(data: Record<string, unknown>) {
182
216
  if (data.dockPosition === "left" || data.dockPosition === "bottom" || data.dockPosition === "right") {
183
217
  patch.dockPosition = data.dockPosition;
184
218
  }
219
+ const dbExpanded = sanitizeDbExpanded(data.dbSidebarExpanded);
220
+ if (dbExpanded) patch.dbSidebarExpanded = dbExpanded;
185
221
  if (Object.keys(patch).length === 0) return;
186
222
  persistSettings(patch);
187
223
  useSettingsStore.setState(patch as Partial<SettingsState>);
@@ -205,6 +241,7 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
205
241
  sidebarTabOrder: sanitizeTabOrder(_initial.sidebarTabOrder),
206
242
  jiraEnabled: _initial.jiraEnabled ?? false,
207
243
  dockPosition: (_initial.dockPosition === "left" || _initial.dockPosition === "right") ? _initial.dockPosition : "bottom",
244
+ dbSidebarExpanded: sanitizeDbExpanded(_initial.dbSidebarExpanded) ?? DEFAULT_DB_EXPANDED,
208
245
  deviceName: null,
209
246
  version: null,
210
247
  tunnelActive: false,
@@ -339,6 +376,12 @@ export const useSettingsStore = create<SettingsState>((set, get) => ({
339
376
  set({ dockPosition: position });
340
377
  },
341
378
 
379
+ setDbSidebarExpanded: (next) => {
380
+ const clean = sanitizeDbExpanded(next) ?? DEFAULT_DB_EXPANDED;
381
+ persistUiPref({ dbSidebarExpanded: clean });
382
+ set({ dbSidebarExpanded: clean });
383
+ },
384
+
342
385
  fetchServerInfo: async () => {
343
386
  try {
344
387
  const token = getAuthToken();