@hienlh/ppm 0.12.0 → 0.12.1
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 +5 -0
- package/dist/web/assets/{audio-preview-D4AxF10w.js → audio-preview-DBOZOGw7.js} +1 -1
- package/dist/web/assets/{chat-tab-Bq2hmJ-B.js → chat-tab-BrdZcg-D.js} +3 -3
- package/dist/web/assets/{code-editor-CMcDjype.js → code-editor-BjB4rgPf.js} +2 -2
- package/dist/web/assets/{conflict-editor-Br-ugFiK.js → conflict-editor-BnXrp55a.js} +1 -1
- package/dist/web/assets/{database-viewer-DxP0GmQK.js → database-viewer-D39etQBx.js} +1 -1
- package/dist/web/assets/{diff-viewer-oEyE9UwV.js → diff-viewer-NVkEMuQW.js} +1 -1
- package/dist/web/assets/{extension-webview-CVqfQGjg.js → extension-webview-AjxJYBif.js} +1 -1
- package/dist/web/assets/{image-preview-CY3sVd25.js → image-preview-BDhESiBw.js} +1 -1
- package/dist/web/assets/index-BeD-Flot.js +23 -0
- package/dist/web/assets/{markdown-renderer-DwqWhkri.js → markdown-renderer-CRA2pyBy.js} +1 -1
- package/dist/web/assets/{pdf-preview-Cl95qWE_.js → pdf-preview-BbA8cq4C.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-iJ3MAjXa.js → port-forwarding-tab-D-Ka0Xhw.js} +1 -1
- package/dist/web/assets/{postgres-viewer-Do_w0Cji.js → postgres-viewer-B4YwHwTt.js} +1 -1
- package/dist/web/assets/{settings-tab-DyBeLmUh.js → settings-tab-ycdscbD8.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-oZkGJfW2.js → sqlite-viewer-CBR5cebG.js} +1 -1
- package/dist/web/assets/{terminal-tab-UoDiWvzG.js → terminal-tab-DATStOUT.js} +1 -1
- package/dist/web/assets/{video-preview-3MbkDYcA.js → video-preview-B-DptaDM.js} +1 -1
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/web/components/layout/draggable-tab.tsx +12 -0
- package/src/web/components/layout/tab-bar.tsx +66 -0
- package/dist/web/assets/index-BDRoldC9.js +0 -23
|
@@ -20,6 +20,12 @@ import { useTabDrag } from "@/hooks/use-tab-drag";
|
|
|
20
20
|
import { useTouchTabDrag, wasTouchDragRecent } from "@/hooks/use-touch-tab-drag";
|
|
21
21
|
import { openCommandPalette } from "@/hooks/use-global-keybindings";
|
|
22
22
|
import { api, projectUrl } from "@/lib/api-client";
|
|
23
|
+
import { useProjectTags } from "@/components/chat/tag-filter-chips";
|
|
24
|
+
import {
|
|
25
|
+
ContextMenuSub, ContextMenuSubTrigger, ContextMenuSubContent,
|
|
26
|
+
ContextMenuItem, ContextMenuSeparator,
|
|
27
|
+
} from "@/components/ui/context-menu";
|
|
28
|
+
import { Tag, Check } from "lucide-react";
|
|
23
29
|
import { useNotificationStore, notificationColor } from "@/stores/notification-store";
|
|
24
30
|
import { useStreamingStore } from "@/stores/streaming-store";
|
|
25
31
|
import { useTabOverflow, getHiddenUnreadDirection } from "@/hooks/use-tab-overflow";
|
|
@@ -64,6 +70,37 @@ export const TabBar = memo(function TabBar({ panelId }: TabBarProps) {
|
|
|
64
70
|
useTabDrag(effectivePanelId);
|
|
65
71
|
const { handleTouchStart, handleTouchMove, handleTouchEnd } = useTouchTabDrag(effectivePanelId);
|
|
66
72
|
|
|
73
|
+
const { projectTags, loadTags } = useProjectTags(activeProject?.name);
|
|
74
|
+
const [sessionTagMap, setSessionTagMap] = useState<Record<string, { id: number; name: string; color: string }>>({});
|
|
75
|
+
|
|
76
|
+
// Fetch session tags for open chat tabs
|
|
77
|
+
const chatSessionIds = tabs.filter((t) => t.type === "chat" && t.metadata?.sessionId).map((t) => t.metadata!.sessionId as string);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!activeProject?.name || chatSessionIds.length === 0) return;
|
|
80
|
+
api.get<{ sessions: { id: string; tag?: { id: number; name: string; color: string } | null }[] }>(
|
|
81
|
+
`${projectUrl(activeProject.name)}/chat/sessions?limit=50`,
|
|
82
|
+
).then((data) => {
|
|
83
|
+
const map: Record<string, { id: number; name: string; color: string }> = {};
|
|
84
|
+
for (const s of data.sessions) { if (s.tag) map[s.id] = s.tag; }
|
|
85
|
+
setSessionTagMap(map);
|
|
86
|
+
}).catch(() => {});
|
|
87
|
+
}, [activeProject?.name, chatSessionIds.join(",")]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
88
|
+
|
|
89
|
+
const assignTagToSession = useCallback(async (sessionId: string, tagId: number | null) => {
|
|
90
|
+
if (!activeProject?.name) return;
|
|
91
|
+
try {
|
|
92
|
+
if (tagId !== null) {
|
|
93
|
+
await api.patch(`${projectUrl(activeProject.name)}/chat/sessions/${sessionId}/tag`, { tagId });
|
|
94
|
+
const tag = projectTags.find((t) => t.id === tagId);
|
|
95
|
+
if (tag) setSessionTagMap((prev) => ({ ...prev, [sessionId]: { id: tag.id, name: tag.name, color: tag.color } }));
|
|
96
|
+
} else {
|
|
97
|
+
await api.del(`${projectUrl(activeProject.name)}/chat/sessions/${sessionId}/tag`);
|
|
98
|
+
setSessionTagMap((prev) => { const n = { ...prev }; delete n[sessionId]; return n; });
|
|
99
|
+
}
|
|
100
|
+
loadTags();
|
|
101
|
+
} catch { /* silent */ }
|
|
102
|
+
}, [activeProject?.name, projectTags, loadTags]);
|
|
103
|
+
|
|
67
104
|
const notifications = useNotificationStore((s) => s.notifications);
|
|
68
105
|
const streamingSessions = useStreamingStore((s) => s.sessions);
|
|
69
106
|
const { canScrollLeft, canScrollRight, scrollLeft: doScrollLeft, scrollRight: doScrollRight } =
|
|
@@ -219,6 +256,35 @@ export const TabBar = memo(function TabBar({ panelId }: TabBarProps) {
|
|
|
219
256
|
}}
|
|
220
257
|
onRename={tab.type === "chat" ? (title) => handleRenameTab(tab, title) : undefined}
|
|
221
258
|
onContextAction={(action) => handleTabContextAction(tab, action)}
|
|
259
|
+
tagColor={sessionId ? sessionTagMap[sessionId]?.color : undefined}
|
|
260
|
+
extraMenuContent={sessionId && projectTags.length > 0 ? (
|
|
261
|
+
<>
|
|
262
|
+
<ContextMenuSub>
|
|
263
|
+
<ContextMenuSubTrigger>
|
|
264
|
+
<Tag className="size-3.5 mr-2" />
|
|
265
|
+
Set Tag
|
|
266
|
+
</ContextMenuSubTrigger>
|
|
267
|
+
<ContextMenuSubContent>
|
|
268
|
+
{projectTags.map((pt) => (
|
|
269
|
+
<ContextMenuItem key={pt.id} onClick={() => assignTagToSession(sessionId, pt.id)}>
|
|
270
|
+
<span className="size-2.5 rounded-full mr-2 shrink-0" style={{ backgroundColor: pt.color }} />
|
|
271
|
+
{pt.name}
|
|
272
|
+
{sessionTagMap[sessionId]?.id === pt.id && <Check className="size-3 ml-auto" />}
|
|
273
|
+
</ContextMenuItem>
|
|
274
|
+
))}
|
|
275
|
+
{sessionTagMap[sessionId] && (
|
|
276
|
+
<>
|
|
277
|
+
<ContextMenuSeparator />
|
|
278
|
+
<ContextMenuItem onClick={() => assignTagToSession(sessionId, null)}>
|
|
279
|
+
Remove tag
|
|
280
|
+
</ContextMenuItem>
|
|
281
|
+
</>
|
|
282
|
+
)}
|
|
283
|
+
</ContextMenuSubContent>
|
|
284
|
+
</ContextMenuSub>
|
|
285
|
+
<ContextMenuSeparator />
|
|
286
|
+
</>
|
|
287
|
+
) : undefined}
|
|
222
288
|
/>
|
|
223
289
|
);
|
|
224
290
|
})}
|