@yolk_vat-y/dsh-project-memory 0.4.3 → 0.4.4

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.
@@ -1,192 +0,0 @@
1
- /**
2
- * Task Panel Client Store(自包含:不依赖宿主 store 包)
3
- * Reactive store for task list, bound task, UI state.
4
- * 数据由 /tasks、/task 命令执行结果(JSON 载荷)写入,localStorage 持久化 UI 状态。
5
- */
6
- import { useSyncExternalStore } from 'react'
7
-
8
- export interface TaskStep {
9
- content: string
10
- status: 'pending' | 'in_progress' | 'completed'
11
- }
12
-
13
- export interface TaskFile {
14
- path: string
15
- line?: number
16
- }
17
-
18
- export interface Task {
19
- id: string
20
- title: string
21
- steps: TaskStep[]
22
- files: TaskFile[]
23
- lastActiveAt: string
24
- updatedAt: string
25
- archived?: boolean
26
- }
27
-
28
- export interface TaskPanelState {
29
- tasks: Task[]
30
- boundTaskId: string | null
31
- archivedCount: number
32
- // UI state
33
- expandedTaskIds: string[]
34
- panelPosition: { x: number; y: number }
35
- /** true = 折叠成顶部迷你条(仍可见、可拖拽)。 */
36
- minimized: boolean
37
- /** true = 彻底隐藏(无迷你条),仅可通过 /task /tasks 或重新加载唤起。 */
38
- closed: boolean
39
- /** 卡片强调色主题:native 或 accent 名(azure/violet/teal/rose)。 */
40
- theme: string
41
- lastUpdate: number
42
- }
43
-
44
- /** /tasks 命令响应(text)里夹带的 JSON 载荷结构。 */
45
- export interface TaskPayload {
46
- tasks: Task[]
47
- boundId: string | null
48
- archived: number
49
- }
50
-
51
- /** 从命令输出文本中解析任务快照 JSON(支持 fenced ```json 块或尾部对象)。 */
52
- export function parseTaskPayloadText(text?: string | null): TaskPayload | null {
53
- if (!text) return null
54
- let jsonBlock: string | null = null
55
- const fenced = text.match(/```(?:json)?\s*\n([\s\S]*?)\n```/)
56
- if (fenced) jsonBlock = fenced[1]
57
- else {
58
- const lastBrace = text.lastIndexOf('{')
59
- if (lastBrace !== -1) jsonBlock = text.slice(lastBrace)
60
- }
61
- if (!jsonBlock) return null
62
- try {
63
- const data = JSON.parse(jsonBlock)
64
- if (!Array.isArray(data.tasks)) return null
65
- return { tasks: data.tasks, boundId: data.boundId ?? null, archived: data.archived ?? 0 }
66
- } catch {
67
- return null
68
- }
69
- }
70
-
71
- const STORAGE_KEY = 'dsh-pm-task-panel-state'
72
-
73
- function defaultPosition(): { x: number; y: number } {
74
- if (typeof window !== 'undefined') {
75
- return { x: Math.max(16, window.innerWidth - 408), y: 72 }
76
- }
77
- return { x: 0, y: 0 }
78
- }
79
-
80
- function getDefaultState(): TaskPanelState {
81
- if (typeof window !== 'undefined') {
82
- try {
83
- const saved = localStorage.getItem(STORAGE_KEY)
84
- if (saved) {
85
- const parsed = JSON.parse(saved)
86
- return {
87
- tasks: Array.isArray(parsed.tasks) ? parsed.tasks : [],
88
- boundTaskId: parsed.boundTaskId ?? null,
89
- archivedCount: parsed.archivedCount ?? 0,
90
- expandedTaskIds: Array.isArray(parsed.expandedTaskIds) ? parsed.expandedTaskIds : [],
91
- panelPosition: parsed.panelPosition ?? defaultPosition(),
92
- minimized: parsed.minimized !== false, // 默认折叠成迷你条
93
- theme: typeof parsed.theme === 'string' ? parsed.theme : 'native',
94
- closed: !!parsed.closed,
95
- lastUpdate: parsed.lastUpdate ?? 0,
96
- }
97
- }
98
- } catch { /* corrupt state — fall through to defaults */ }
99
- }
100
- return {
101
- tasks: [],
102
- boundTaskId: null,
103
- archivedCount: 0,
104
- expandedTaskIds: [],
105
- panelPosition: defaultPosition(),
106
- minimized: true, // 默认折叠,不打扰
107
- closed: false,
108
- theme: 'native',
109
- lastUpdate: 0,
110
- }
111
- }
112
-
113
- function persist(state: TaskPanelState): void {
114
- if (typeof window === 'undefined') return
115
- try {
116
- localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
117
- } catch { /* storage full/unavailable — non-fatal */ }
118
- }
119
-
120
- let state: TaskPanelState = getDefaultState()
121
- const listeners = new Set<() => void>()
122
-
123
- function setState(next: TaskPanelState): void {
124
- state = next
125
- persist(next)
126
- for (const listener of listeners) listener()
127
- }
128
-
129
- export const taskStore = {
130
- subscribe(listener: () => void): () => void {
131
- listeners.add(listener)
132
- return () => { listeners.delete(listener) }
133
- },
134
- getSnapshot(): TaskPanelState {
135
- return state
136
- },
137
- actions: {
138
- setTasks(payload: { tasks: Task[]; boundTaskId: string | null; archivedCount: number }): void {
139
- const prev = state
140
- setState({
141
- ...prev,
142
- tasks: payload.tasks || [],
143
- boundTaskId: payload.boundTaskId ?? null,
144
- archivedCount: payload.archivedCount ?? 0,
145
- lastUpdate: Date.now(),
146
- })
147
- },
148
- toggleTaskExpanded(taskId: string): void {
149
- const prev = state
150
- const ids = prev.expandedTaskIds.includes(taskId)
151
- ? prev.expandedTaskIds.filter((id) => id !== taskId)
152
- : [...prev.expandedTaskIds, taskId]
153
- setState({ ...prev, expandedTaskIds: ids })
154
- },
155
- expandAll(): void {
156
- const prev = state
157
- setState({ ...prev, expandedTaskIds: prev.tasks.map((t) => t.id) })
158
- },
159
- setPanelPosition(pos: { x: number; y: number }): void {
160
- setState({ ...state, panelPosition: pos })
161
- },
162
- /** 展开面板(同时解除隐藏/折叠)。 */
163
- open(): void {
164
- setState({ ...state, minimized: false, closed: false })
165
- },
166
- /** 折叠成顶部迷你条(仍可见)。 */
167
- minimize(): void {
168
- setState({ ...state, minimized: true, closed: false })
169
- },
170
- /** 彻底隐藏(无迷你条);仅能通过 /task、/tasks 或重新加载唤起。 */
171
- close(): void {
172
- setState({ ...state, minimized: true, closed: true })
173
- },
174
- setTheme(theme: string): void {
175
- setState({ ...state, theme })
176
- },
177
- setMinimized(minimized: boolean): void {
178
- setState({ ...state, minimized, closed: minimized ? state.closed : false })
179
- },
180
- reset(): void {
181
- setState(getDefaultState())
182
- },
183
- },
184
- }
185
-
186
- export function useTaskStore(): TaskPanelState {
187
- return useSyncExternalStore(taskStore.subscribe, taskStore.getSnapshot, taskStore.getSnapshot)
188
- }
189
-
190
- export function useTaskActions() {
191
- return taskStore.actions
192
- }