@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.
- package/CHANGELOG.md +11 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/{ai-resource-editor-CwZAAlN5.js → ai-resource-editor-qnr1gXKX.js} +1 -1
- package/dist/web/assets/{audio-preview-C8ePPqYM.js → audio-preview-IJyTkZhv.js} +1 -1
- package/dist/web/assets/{chat-tab-ByfGBWbW.js → chat-tab-Bf7bP_mH.js} +2 -2
- package/dist/web/assets/{code-editor-BLUWMpZn.js → code-editor-DbgRk7IQ.js} +2 -2
- package/dist/web/assets/{conflict-editor-B1oxNu29.js → conflict-editor-7379QQTo.js} +1 -1
- package/dist/web/assets/{csv-preview-CrGymnn9.js → csv-preview-CkZtgJJd.js} +1 -1
- package/dist/web/assets/{database-viewer-BTofJwov.js → database-viewer-BOWXg1eU.js} +1 -1
- package/dist/web/assets/{diff-viewer-Bsp25uLy.js → diff-viewer-3q8WtMhX.js} +1 -1
- package/dist/web/assets/{docx-preview-BULrDLso.js → docx-preview-uAcT8IrY.js} +1 -1
- package/dist/web/assets/{extension-webview-gByeDwuJ.js → extension-webview-C9B286ef.js} +1 -1
- package/dist/web/assets/{git-log-panel-BjB3uGkM.js → git-log-panel-CWfaQmIt.js} +1 -1
- package/dist/web/assets/{glide-data-grid-BmofPEFh.js → glide-data-grid-BTbN5x8y.js} +1 -1
- package/dist/web/assets/{group-chat-tab-DczxC8y0.js → group-chat-tab-CD1tHFCt.js} +1 -1
- package/dist/web/assets/{image-preview-C2JNJTTR.js → image-preview-D35tUSfv.js} +1 -1
- package/dist/web/assets/index-DMEso_fH.js +50 -0
- package/dist/web/assets/{keybindings-store-DWwuOAWb.js → keybindings-store-C56hagRp.js} +1 -1
- package/dist/web/assets/{markdown-renderer-BGumVkCU.js → markdown-renderer-CaVoLF6f.js} +1 -1
- package/dist/web/assets/{markdown-renderer-CvQmV_TU.js → markdown-renderer-w7cIJc1V.js} +1 -1
- package/dist/web/assets/{notification-store-OaYhYp1C.js → notification-store-CijmEBze.js} +1 -1
- package/dist/web/assets/{pdf-preview-DjWTE3jF.js → pdf-preview-URqY4tEa.js} +1 -1
- package/dist/web/assets/{postgres-viewer-K6ZyFyso.js → postgres-viewer-D3iSQpJq.js} +1 -1
- package/dist/web/assets/settings-store-UwT_fNhg.js +2 -0
- package/dist/web/assets/{settings-tab-DcUZ7ln3.js → settings-tab-BMAlaxBD.js} +1 -1
- package/dist/web/assets/{sql-query-editor-D9pM_Dys.js → sql-query-editor-Qrom7wG2.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-zudsiPxP.js → sqlite-viewer-CPzA2RA4.js} +1 -1
- package/dist/web/assets/{system-monitor-tab-C7VZxGbH.js → system-monitor-tab-DEXXqZIz.js} +1 -1
- package/dist/web/assets/{terminal-tab-xiwZMQLE.js → terminal-tab-Cn1szPMh.js} +1 -1
- package/dist/web/assets/{tool-cards-C4DU4njU.js → tool-cards-DNftJX5Y.js} +2 -2
- package/dist/web/assets/{use-monaco-theme-D6Z2faTR.js → use-monaco-theme-XO5ZyYBW.js} +1 -1
- package/dist/web/assets/{video-preview-BL-sWnxi.js → video-preview-U2aMrCxv.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/cli/commands/status.ts +21 -2
- package/src/server/routes/settings.ts +11 -0
- package/src/services/autostart-generator.ts +5 -0
- package/src/services/supervisor.ts +61 -28
- package/src/web/components/database/connection-list.tsx +50 -28
- package/src/web/components/database/use-db-tree-expansion.ts +74 -0
- package/src/web/stores/settings-store.ts +43 -0
- package/dist/web/assets/index-D6qn4rtk.js +0 -50
- 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();
|