@hienlh/ppm 0.13.48 → 0.13.50

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 (61) hide show
  1. package/CHANGELOG.md +8 -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/{audio-preview-BFc4v5Tx.js → audio-preview-CILFIsuu.js} +2 -2
  5. package/dist/web/assets/{audio-preview-BFc4v5Tx.js.map → audio-preview-CILFIsuu.js.map} +1 -1
  6. package/dist/web/assets/{chat-tab-x1rBGHj5.js → chat-tab-DBYwH_Aa.js} +4 -4
  7. package/dist/web/assets/{chat-tab-x1rBGHj5.js.map → chat-tab-DBYwH_Aa.js.map} +1 -1
  8. package/dist/web/assets/{code-editor-BgyAZcQX.js → code-editor-MXnkYRLp.js} +3 -3
  9. package/dist/web/assets/{code-editor-BgyAZcQX.js.map → code-editor-MXnkYRLp.js.map} +1 -1
  10. package/dist/web/assets/{conflict-editor-Dhc1lem7.js → conflict-editor-C6wH5wV6.js} +2 -2
  11. package/dist/web/assets/{conflict-editor-Dhc1lem7.js.map → conflict-editor-C6wH5wV6.js.map} +1 -1
  12. package/dist/web/assets/{database-viewer-DnnjO38W.js → database-viewer-BjUruZLv.js} +2 -2
  13. package/dist/web/assets/{database-viewer-DnnjO38W.js.map → database-viewer-BjUruZLv.js.map} +1 -1
  14. package/dist/web/assets/{diff-viewer-B6q9wXD6.js → diff-viewer-B_nU7bQi.js} +2 -2
  15. package/dist/web/assets/{diff-viewer-B6q9wXD6.js.map → diff-viewer-B_nU7bQi.js.map} +1 -1
  16. package/dist/web/assets/{extension-webview-CMBEb4FF.js → extension-webview-B56ZfvoD.js} +2 -2
  17. package/dist/web/assets/{extension-webview-CMBEb4FF.js.map → extension-webview-B56ZfvoD.js.map} +1 -1
  18. package/dist/web/assets/{glide-data-grid-BLcBAxgp.js → glide-data-grid-D-qQqqp7.js} +2 -2
  19. package/dist/web/assets/{glide-data-grid-BLcBAxgp.js.map → glide-data-grid-D-qQqqp7.js.map} +1 -1
  20. package/dist/web/assets/{image-preview-YpWk7AOb.js → image-preview-Dc6AiqYX.js} +2 -2
  21. package/dist/web/assets/{image-preview-YpWk7AOb.js.map → image-preview-Dc6AiqYX.js.map} +1 -1
  22. package/dist/web/assets/{index-hxAGD2rx.js → index-8_rE2Q1-.js} +6 -6
  23. package/dist/web/assets/{index-hxAGD2rx.js.map → index-8_rE2Q1-.js.map} +1 -1
  24. package/dist/web/assets/keybindings-store-COJD5O6M.js +1 -0
  25. package/dist/web/assets/{markdown-renderer-BAAmsTL9.js → markdown-renderer-CNQ8I0Dk.js} +2 -2
  26. package/dist/web/assets/{markdown-renderer-BAAmsTL9.js.map → markdown-renderer-CNQ8I0Dk.js.map} +1 -1
  27. package/dist/web/assets/notification-store-BiZaLXop.js +1 -0
  28. package/dist/web/assets/{panel-store-Dy8-7E_g.js → panel-store-C8wwxBpn.js} +2 -2
  29. package/dist/web/assets/{panel-store-Dy8-7E_g.js.map → panel-store-C8wwxBpn.js.map} +1 -1
  30. package/dist/web/assets/{pdf-preview-DWe7ARXI.js → pdf-preview-zs9QdgDp.js} +2 -2
  31. package/dist/web/assets/{pdf-preview-DWe7ARXI.js.map → pdf-preview-zs9QdgDp.js.map} +1 -1
  32. package/dist/web/assets/{port-forwarding-tab-R5V7zkKO.js → port-forwarding-tab-sArYx1nt.js} +2 -2
  33. package/dist/web/assets/{port-forwarding-tab-R5V7zkKO.js.map → port-forwarding-tab-sArYx1nt.js.map} +1 -1
  34. package/dist/web/assets/{postgres-viewer-AxCUyDQP.js → postgres-viewer-khk7N7cd.js} +2 -2
  35. package/dist/web/assets/{postgres-viewer-AxCUyDQP.js.map → postgres-viewer-khk7N7cd.js.map} +1 -1
  36. package/dist/web/assets/{settings-tab-CJ6w6q2s.js → settings-tab-CGWhVzQm.js} +1 -1
  37. package/dist/web/assets/{sql-query-editor-DhZvNbKv.js → sql-query-editor-B5Ndypxp.js} +2 -2
  38. package/dist/web/assets/{sql-query-editor-DhZvNbKv.js.map → sql-query-editor-B5Ndypxp.js.map} +1 -1
  39. package/dist/web/assets/{sqlite-viewer-Qv8TlhPb.js → sqlite-viewer-BkpONSGa.js} +2 -2
  40. package/dist/web/assets/{sqlite-viewer-Qv8TlhPb.js.map → sqlite-viewer-BkpONSGa.js.map} +1 -1
  41. package/dist/web/assets/{tab-store-Dtg1_qL0.js → tab-store-CNas5Ny8.js} +2 -2
  42. package/dist/web/assets/{tab-store-Dtg1_qL0.js.map → tab-store-CNas5Ny8.js.map} +1 -1
  43. package/dist/web/assets/{terminal-tab-9hLQtUUT.js → terminal-tab-BgMCsdeN.js} +2 -2
  44. package/dist/web/assets/{terminal-tab-9hLQtUUT.js.map → terminal-tab-BgMCsdeN.js.map} +1 -1
  45. package/dist/web/assets/{video-preview-C-tj7tok.js → video-preview-w8ZAy8av.js} +2 -2
  46. package/dist/web/assets/{video-preview-C-tj7tok.js.map → video-preview-w8ZAy8av.js.map} +1 -1
  47. package/dist/web/index.html +3 -3
  48. package/dist/web/sw.js +1 -1
  49. package/package.json +1 -1
  50. package/src/services/db.service.ts +28 -0
  51. package/src/types/extension.ts +1 -1
  52. package/src/web/components/settings/extension-manager-section.tsx +2 -2
  53. package/src/web/stores/panel-store.ts +5 -0
  54. package/dist/web/assets/keybindings-store-Dn9ANcCK.js +0 -1
  55. package/dist/web/assets/notification-store-DyVQiPv9.js +0 -1
  56. package/packages/ext-database/package.json +0 -48
  57. package/packages/ext-database/src/connection-tree.ts +0 -201
  58. package/packages/ext-database/src/extension.ts +0 -578
  59. package/packages/ext-database/src/query-panel.ts +0 -133
  60. package/packages/ext-database/src/table-viewer-panel.ts +0 -420
  61. package/packages/ext-database/tsconfig.json +0 -8
@@ -610,6 +610,34 @@ function runMigrations(database: Database): void {
610
610
  try { database.exec("ALTER TABLE session_metadata ADD COLUMN last_known_title TEXT"); } catch { /* column exists */ }
611
611
  database.exec("PRAGMA user_version = 23");
612
612
  }
613
+
614
+ if (current < 25) {
615
+ // Re-create extension tables if missing (e.g. after accidental drop)
616
+ database.exec(`
617
+ CREATE TABLE IF NOT EXISTS extensions (
618
+ id TEXT PRIMARY KEY,
619
+ version TEXT NOT NULL,
620
+ display_name TEXT,
621
+ description TEXT,
622
+ icon TEXT,
623
+ enabled INTEGER DEFAULT 1,
624
+ manifest TEXT NOT NULL,
625
+ installed_at TEXT DEFAULT (datetime('now')),
626
+ updated_at TEXT DEFAULT (datetime('now'))
627
+ );
628
+
629
+ CREATE TABLE IF NOT EXISTS extension_storage (
630
+ ext_id TEXT NOT NULL,
631
+ scope TEXT NOT NULL,
632
+ key TEXT NOT NULL,
633
+ value TEXT,
634
+ PRIMARY KEY (ext_id, scope, key),
635
+ FOREIGN KEY (ext_id) REFERENCES extensions(id) ON DELETE CASCADE
636
+ );
637
+
638
+ PRAGMA user_version = 25;
639
+ `);
640
+ }
613
641
  }
614
642
 
615
643
  // ---------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  /** Extension manifest — parsed from package.json of an installed extension */
2
2
  export interface ExtensionManifest {
3
- id: string; // npm package name, e.g. @ppm/ext-database
3
+ id: string; // npm package name, e.g. @ppm/ext-myext
4
4
  version: string;
5
5
  main: string; // JS entry point relative to extension root
6
6
  displayName?: string;
@@ -97,7 +97,7 @@ export function ExtensionManagerSection() {
97
97
  value={installName}
98
98
  onChange={(e) => setInstallName(e.target.value)}
99
99
  onKeyDown={(e) => { if (e.key === "Enter") handleInstall(); }}
100
- placeholder="npm package name (e.g. @ppm/ext-database)"
100
+ placeholder="npm package name (e.g. @ppm/ext-myext)"
101
101
  className="h-8 text-xs flex-1"
102
102
  />
103
103
  <Button
@@ -122,7 +122,7 @@ export function ExtensionManagerSection() {
122
122
  <Input
123
123
  value={devPath}
124
124
  onChange={(e) => setDevPath(e.target.value)}
125
- placeholder="Local path (e.g. ./packages/ext-database)"
125
+ placeholder="Local path (e.g. ./packages/ext-myext)"
126
126
  className="h-8 text-xs flex-1"
127
127
  />
128
128
  <Button
@@ -323,6 +323,11 @@ export const usePanelStore = create<PanelStore>()((set, get) => {
323
323
  if (!panel) return;
324
324
  const pid = panel.id;
325
325
 
326
+ // Clear persisted terminal session so reopening creates a fresh PTY
327
+ if (tabId.startsWith("terminal:")) {
328
+ try { localStorage.removeItem(`ppm:terminal-session:${tabId}`); } catch { /* */ }
329
+ }
330
+
326
331
  set((s) => {
327
332
  const p = s.panels[pid]!;
328
333
  const newTabs = p.tabs.filter((t) => t.id !== tabId);
@@ -1 +0,0 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./api-client-DIhJ5qVW.js";import{T as e}from"./index-hxAGD2rx.js";export{e as useKeybindingsStore};
@@ -1 +0,0 @@
1
- import"./vendor-markdown-0Mxgxy0L.js";import"./api-client-DIhJ5qVW.js";import{j as e}from"./index-hxAGD2rx.js";export{e as useNotificationStore};
@@ -1,48 +0,0 @@
1
- {
2
- "name": "@ppm/ext-database",
3
- "version": "0.1.0",
4
- "main": "src/extension.ts",
5
- "engines": { "ppm": ">=0.9.0" },
6
- "activationEvents": ["onCommand:ppm-db.openViewer", "onView:ppm-db.connections"],
7
- "contributes": {
8
- "commands": [
9
- { "command": "ppm-db.openViewer", "title": "Database: Open Viewer" },
10
- { "command": "ppm-db.runQuery", "title": "Database: Run SQL Query" },
11
- { "command": "ppm-db.refreshConnections", "title": "Database: Refresh Connections", "icon": "refresh" },
12
- { "command": "ppm-db.addConnection", "title": "Add connection", "icon": "plus" },
13
- { "command": "ppm-db.editConnection", "title": "Database: Edit Connection" },
14
- { "command": "ppm-db.deleteConnection", "title": "Database: Delete Connection" },
15
- { "command": "ppm-db.testConnection", "title": "Database: Test Connection" },
16
- { "command": "ppm-db.exportConnections", "title": "Database: Export Connections" },
17
- { "command": "ppm-db.importConnections", "title": "Database: Import Connections" }
18
- ],
19
- "views": {
20
- "sidebar": [
21
- { "id": "ppm-db.connections", "name": "DB Connections", "type": "tree" }
22
- ]
23
- },
24
- "menus": {
25
- "commandPalette": [
26
- { "command": "ppm-db.openViewer" },
27
- { "command": "ppm-db.runQuery" },
28
- { "command": "ppm-db.exportConnections" },
29
- { "command": "ppm-db.importConnections" }
30
- ],
31
- "view/title": [
32
- { "command": "ppm-db.refreshConnections", "when": "view == ppm-db.connections", "group": "navigation" },
33
- { "command": "ppm-db.addConnection", "when": "view == ppm-db.connections", "group": "navigation" }
34
- ]
35
- },
36
- "configuration": {
37
- "properties": {
38
- "ppm-db.maxRows": { "type": "number", "default": 100, "description": "Max rows to display" },
39
- "ppm-db.autoConnect": { "type": "boolean", "default": true, "description": "Auto-connect on open" }
40
- }
41
- }
42
- },
43
- "ppm": {
44
- "displayName": "Database Viewer",
45
- "icon": "database",
46
- "webviewDir": "webview"
47
- }
48
- }
@@ -1,201 +0,0 @@
1
- /**
2
- * TreeDataProvider for database connections → tables → columns.
3
- * Fetches data from PPM REST API (/api/db/*).
4
- */
5
-
6
- interface ConnectionNode {
7
- id: string;
8
- name: string;
9
- type: "connection" | "group" | "table" | "column";
10
- connectionId?: number;
11
- connectionName?: string;
12
- connectionType?: string;
13
- connectionColor?: string | null;
14
- connectionReadonly?: number;
15
- groupName?: string | null;
16
- schemaName?: string;
17
- dataType?: string;
18
- rowCount?: number;
19
- children?: ConnectionNode[];
20
- }
21
-
22
- interface ApiConnection {
23
- id: number;
24
- name: string;
25
- type: string;
26
- color: string | null;
27
- readonly: number;
28
- group_name: string | null;
29
- }
30
-
31
- interface ApiTable {
32
- name: string;
33
- schema: string;
34
- rowCount: number;
35
- }
36
-
37
- interface ApiColumn {
38
- name: string;
39
- type: string;
40
- nullable: boolean;
41
- pk: boolean;
42
- defaultValue: string | null;
43
- }
44
-
45
- export class ConnectionTreeProvider {
46
- private _onDidChange: { fire: (el?: ConnectionNode) => void; event: unknown };
47
- private baseUrl: string;
48
-
49
- constructor(eventEmitter: { fire: (el?: ConnectionNode) => void; event: unknown }, baseUrl = "") {
50
- this._onDidChange = eventEmitter;
51
- this.baseUrl = baseUrl;
52
- }
53
-
54
- get onDidChangeTreeData() {
55
- return this._onDidChange.event;
56
- }
57
-
58
- refresh(): void {
59
- this._onDidChange.fire(undefined);
60
- }
61
-
62
- async getChildren(element?: ConnectionNode): Promise<ConnectionNode[]> {
63
- if (!element) return this.getRootNodes();
64
- if (element.type === "group") return element.children ?? [];
65
- if (element.type === "connection") return this.getTables(element);
66
- if (element.type === "table") return this.getColumns(element);
67
- return [];
68
- }
69
-
70
- getTreeItem(element: ConnectionNode): Record<string, unknown> {
71
- const isConn = element.type === "connection";
72
- const isGroup = element.type === "group";
73
- const isTable = element.type === "table";
74
- const isCol = element.type === "column";
75
-
76
- // Table description: row count
77
- let description: string | undefined;
78
- if (isCol) description = element.dataType;
79
- else if (isTable && element.rowCount !== undefined) description = `${element.rowCount.toLocaleString()} rows`;
80
-
81
- // Connection badge: type + readonly
82
- let badge: string | undefined;
83
- if (isConn) {
84
- badge = element.connectionType === "postgres" ? "PG" : "DB";
85
- if (element.connectionReadonly === 1) badge += " 🔒";
86
- }
87
-
88
- return {
89
- id: element.id,
90
- label: element.name,
91
- description,
92
- collapsibleState: isCol ? "none" : "collapsed",
93
- contextValue: element.type,
94
- command: isTable ? "ppm-db.openViewer" : undefined,
95
- commandArgs: isTable
96
- ? [element.connectionId, element.connectionName ?? "Database", element.name, element.schemaName ?? "public"]
97
- : undefined,
98
- color: isConn ? (element.connectionColor ?? undefined) : undefined,
99
- badge,
100
- actions: isConn ? [
101
- { icon: "refresh", tooltip: "Refresh tables", command: "ppm-db.refreshConnection", commandArgs: [element.connectionId] },
102
- { icon: "edit", tooltip: "Edit connection", command: "ppm-db.editConnection", commandArgs: [element.connectionId] },
103
- { icon: "trash", tooltip: "Delete connection", command: "ppm-db.deleteConnection", commandArgs: [element.connectionId, element.name] },
104
- ] : isGroup ? [] : undefined,
105
- };
106
- }
107
-
108
- /** Build root tree: group nodes wrapping connection nodes, or flat if no groups */
109
- private async getRootNodes(): Promise<ConnectionNode[]> {
110
- const connections = await this.getConnections();
111
- // Group by group_name
112
- const groups = new Map<string, ConnectionNode[]>();
113
- for (const conn of connections) {
114
- const key = conn.groupName ?? "__ungrouped__";
115
- const list = groups.get(key) ?? [];
116
- list.push(conn);
117
- groups.set(key, list);
118
- }
119
- // If only one group (ungrouped), return connections flat
120
- if (groups.size <= 1 && groups.has("__ungrouped__")) {
121
- return connections;
122
- }
123
- // Build group nodes
124
- const result: ConnectionNode[] = [];
125
- const sortedKeys = Array.from(groups.keys()).sort((a, b) => {
126
- if (a === "__ungrouped__") return 1;
127
- if (b === "__ungrouped__") return -1;
128
- return a.localeCompare(b);
129
- });
130
- for (const key of sortedKeys) {
131
- const label = key === "__ungrouped__" ? "Ungrouped" : key;
132
- result.push({
133
- id: `group:${key}`,
134
- name: label,
135
- type: "group",
136
- children: groups.get(key) ?? [],
137
- });
138
- }
139
- return result;
140
- }
141
-
142
- private async getConnections(): Promise<ConnectionNode[]> {
143
- try {
144
- const res = await fetch(`${this.baseUrl}/api/db/connections`);
145
- const json = await res.json() as { ok: boolean; data?: ApiConnection[] };
146
- if (!json.ok || !json.data) return [];
147
- return json.data.map((c) => ({
148
- id: `conn:${c.id}`,
149
- name: c.name,
150
- type: "connection" as const,
151
- connectionId: c.id,
152
- connectionType: c.type,
153
- connectionColor: c.color,
154
- connectionReadonly: c.readonly,
155
- groupName: c.group_name,
156
- }));
157
- } catch {
158
- return [];
159
- }
160
- }
161
-
162
- private async getTables(conn: ConnectionNode): Promise<ConnectionNode[]> {
163
- try {
164
- const res = await fetch(`${this.baseUrl}/api/db/connections/${conn.connectionId}/tables`);
165
- const json = await res.json() as { ok: boolean; data?: ApiTable[] };
166
- if (!json.ok || !json.data) return [];
167
- return json.data.map((t) => ({
168
- id: `table:${conn.connectionId}:${t.schema}.${t.name}`,
169
- name: t.name,
170
- type: "table" as const,
171
- connectionId: conn.connectionId,
172
- connectionName: conn.name,
173
- connectionType: conn.connectionType,
174
- schemaName: t.schema,
175
- rowCount: t.rowCount,
176
- }));
177
- } catch {
178
- return [];
179
- }
180
- }
181
-
182
- private async getColumns(table: ConnectionNode): Promise<ConnectionNode[]> {
183
- try {
184
- const schema = table.schemaName ?? "public";
185
- const res = await fetch(
186
- `${this.baseUrl}/api/db/connections/${table.connectionId}/schema?table=${encodeURIComponent(table.name)}&schema=${schema}`,
187
- );
188
- const json = await res.json() as { ok: boolean; data?: ApiColumn[] };
189
- if (!json.ok || !json.data) return [];
190
- return json.data.map((c) => ({
191
- id: `col:${table.connectionId}:${table.name}.${c.name}`,
192
- name: c.name,
193
- type: "column" as const,
194
- connectionId: table.connectionId,
195
- dataType: c.type + (c.pk ? " PK" : ""),
196
- }));
197
- } catch {
198
- return [];
199
- }
200
- }
201
- }