@hienlh/ppm 0.13.0 → 0.13.3

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 (51) hide show
  1. package/CHANGELOG.md +30 -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-J5neETTY.js → audio-preview-R7cq1uhJ.js} +1 -1
  5. package/dist/web/assets/chat-tab-umei1UkV.js +12 -0
  6. package/dist/web/assets/{code-editor-tMfcFaQ5.js → code-editor-BTosKXkr.js} +2 -2
  7. package/dist/web/assets/{conflict-editor-FydCxWTC.js → conflict-editor-dzofjxab.js} +1 -1
  8. package/dist/web/assets/{database-viewer-Celi1puH.js → database-viewer-5Uf8Rrls.js} +1 -1
  9. package/dist/web/assets/{diff-viewer-NgDJLTk9.js → diff-viewer-DKLeIBkK.js} +1 -1
  10. package/dist/web/assets/{extension-webview-xWAdCj3q.js → extension-webview-HILvTnnn.js} +1 -1
  11. package/dist/web/assets/{image-preview-C6bFkdZD.js → image-preview-0cJMnFZK.js} +1 -1
  12. package/dist/web/assets/index-Bce0weeW.css +2 -0
  13. package/dist/web/assets/index-DDBvHVVr.js +27 -0
  14. package/dist/web/assets/{markdown-renderer-BAnnk1pI.js → markdown-renderer-D0MrsVJB.js} +1 -1
  15. package/dist/web/assets/{pdf-preview-BNuFTSOL.js → pdf-preview-BBVDS-z5.js} +1 -1
  16. package/dist/web/assets/{port-forwarding-tab-BbDlGxAs.js → port-forwarding-tab-ByKzBs-R.js} +1 -1
  17. package/dist/web/assets/{postgres-viewer-Cman1YRO.js → postgres-viewer-BnCbdR7g.js} +1 -1
  18. package/dist/web/assets/{settings-tab-n5X_Dbu4.js → settings-tab-BPdzUw3v.js} +1 -1
  19. package/dist/web/assets/{sqlite-viewer-D6JT11uu.js → sqlite-viewer-D6mSIIx2.js} +1 -1
  20. package/dist/web/assets/{terminal-tab-B4kMthYo.js → terminal-tab-BLIA53mt.js} +1 -1
  21. package/dist/web/assets/{video-preview-BftQOOzF.js → video-preview-CKaht6nI.js} +1 -1
  22. package/dist/web/index.html +2 -2
  23. package/dist/web/sw.js +1 -1
  24. package/package.json +1 -1
  25. package/src/index.ts +0 -0
  26. package/src/providers/claude-agent-sdk.ts +5 -2
  27. package/src/server/index.ts +12 -4
  28. package/src/server/routes/chat.ts +10 -1
  29. package/src/server/ws/chat.ts +29 -2
  30. package/src/services/autostart-generator.ts +3 -1
  31. package/src/services/autostart-register.ts +17 -0
  32. package/src/services/sd-notify.ts +27 -0
  33. package/src/services/supervisor.ts +31 -5
  34. package/src/types/chat.ts +1 -1
  35. package/src/web/components/chat/chat-history-bar.tsx +2 -9
  36. package/src/web/components/chat/chat-history-panel.tsx +2 -9
  37. package/src/web/components/chat/chat-welcome.tsx +1 -18
  38. package/src/web/components/chat/message-list.tsx +6 -5
  39. package/src/web/components/layout/command-palette.tsx +35 -12
  40. package/src/web/components/layout/draggable-tab.tsx +12 -5
  41. package/src/web/hooks/use-chat.ts +6 -0
  42. package/src/web/hooks/use-notification-badge.ts +7 -7
  43. package/src/web/lib/favicon.ts +37 -15
  44. package/src/web/lib/format-date.ts +21 -0
  45. package/src/web/lib/score-file-search.ts +41 -21
  46. package/src/web/styles/globals.css +12 -0
  47. package/bun.lock +0 -2062
  48. package/bunfig.toml +0 -2
  49. package/dist/web/assets/chat-tab-sVHRa1Fz.js +0 -12
  50. package/dist/web/assets/index-BMhiElt6.css +0 -2
  51. package/dist/web/assets/index-DtbAoxyy.js +0 -23
@@ -6,6 +6,10 @@
6
6
  * > path contains(3) > fuzzy filename(4) > fuzzy path(5)
7
7
  *
8
8
  * Tie-breakers: shorter filename, fewer path segments.
9
+ *
10
+ * Hot-path note: callers pass PRE-LOWERCASED strings to avoid repeated
11
+ * allocations per keystroke. Use `scoreFileSearch` (convenience wrapper)
12
+ * for ad-hoc calls; use `scoreFileSearchFast` for the inner loop.
9
13
  */
10
14
 
11
15
  export interface FileSearchScore {
@@ -20,7 +24,7 @@ export interface FileSearchScore {
20
24
  }
21
25
 
22
26
  /** Extract filename from a path */
23
- function getFilename(path: string): string {
27
+ export function getFilename(path: string): string {
24
28
  const i = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
25
29
  return i >= 0 ? path.slice(i + 1) : path;
26
30
  }
@@ -43,42 +47,58 @@ function fuzzyGap(query: string, text: string): number {
43
47
  return gap;
44
48
  }
45
49
 
46
- export function scoreFileSearch(
47
- query: string,
48
- label: string,
49
- path: string,
50
+ /**
51
+ * Fast scoring — requires pre-lowercased inputs. Use for tight loops.
52
+ * All string params MUST already be lowercase.
53
+ */
54
+ export function scoreFileSearchFast(
55
+ qLower: string,
56
+ filenameLower: string,
57
+ pathLower: string,
58
+ labelLen: number,
59
+ depth: number,
50
60
  ): FileSearchScore | null {
51
- const q = query.toLowerCase();
52
- const nameLower = label.toLowerCase();
53
- const pathLower = path.toLowerCase();
54
- const filename = getFilename(pathLower);
55
- const depth = path.split("/").length;
56
-
57
61
  // Tier 0: exact filename match
58
- if (filename === q) return { tier: 0, offset: 0, nameLen: label.length, depth };
62
+ if (filenameLower === qLower) return { tier: 0, offset: 0, nameLen: labelLen, depth };
59
63
 
60
64
  // Tier 1: filename starts with query
61
- if (filename.startsWith(q)) return { tier: 1, offset: 0, nameLen: label.length, depth };
65
+ if (filenameLower.startsWith(qLower)) return { tier: 1, offset: 0, nameLen: labelLen, depth };
62
66
 
63
67
  // Tier 2: filename contains query as substring
64
- const fnIdx = filename.indexOf(q);
65
- if (fnIdx >= 0) return { tier: 2, offset: fnIdx, nameLen: label.length, depth };
68
+ const fnIdx = filenameLower.indexOf(qLower);
69
+ if (fnIdx >= 0) return { tier: 2, offset: fnIdx, nameLen: labelLen, depth };
66
70
 
67
71
  // Tier 3: full path contains query as substring
68
- const pathIdx = pathLower.indexOf(q);
69
- if (pathIdx >= 0) return { tier: 3, offset: pathIdx, nameLen: label.length, depth };
72
+ const pathIdx = pathLower.indexOf(qLower);
73
+ if (pathIdx >= 0) return { tier: 3, offset: pathIdx, nameLen: labelLen, depth };
70
74
 
71
75
  // Tier 4: fuzzy match on filename
72
- const fnGap = fuzzyGap(q, filename);
73
- if (fnGap >= 0) return { tier: 4, offset: fnGap, nameLen: label.length, depth };
76
+ const fnGap = fuzzyGap(qLower, filenameLower);
77
+ if (fnGap >= 0) return { tier: 4, offset: fnGap, nameLen: labelLen, depth };
74
78
 
75
79
  // Tier 5: fuzzy match on full path
76
- const pathGap = fuzzyGap(q, pathLower);
77
- if (pathGap >= 0) return { tier: 5, offset: pathGap, nameLen: label.length, depth };
80
+ const pathGap = fuzzyGap(qLower, pathLower);
81
+ if (pathGap >= 0) return { tier: 5, offset: pathGap, nameLen: labelLen, depth };
78
82
 
79
83
  return null;
80
84
  }
81
85
 
86
+ /** Convenience wrapper — lowers inputs on the fly. Use for ad-hoc calls. */
87
+ export function scoreFileSearch(
88
+ query: string,
89
+ label: string,
90
+ path: string,
91
+ ): FileSearchScore | null {
92
+ const pathLower = path.toLowerCase();
93
+ return scoreFileSearchFast(
94
+ query.toLowerCase(),
95
+ getFilename(pathLower),
96
+ pathLower,
97
+ label.length,
98
+ path.split("/").length,
99
+ );
100
+ }
101
+
82
102
  /** Compare two scores — for Array.sort (ascending = best first) */
83
103
  export function compareScores(a: FileSearchScore, b: FileSearchScore): number {
84
104
  return (
@@ -294,3 +294,15 @@ html, body {
294
294
  @media (prefers-reduced-motion: reduce) {
295
295
  .markdown-content.is-streaming > *:last-child { animation: none; }
296
296
  }
297
+
298
+ /* Tab streaming indicator — 3 dots bouncing inside chat bubble icon (Messenger typing style) */
299
+ @keyframes tabTypingBounce {
300
+ 0%, 60%, 100% { transform: translateY(0); }
301
+ 30% { transform: translateY(-1.5px); }
302
+ }
303
+ .tab-typing-dot {
304
+ animation: tabTypingBounce 1s ease-in-out infinite;
305
+ }
306
+ @media (prefers-reduced-motion: reduce) {
307
+ .tab-typing-dot { animation: none; }
308
+ }