@fyeeme/pi-thinking-ui 1.0.1 → 1.1.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/state.ts CHANGED
@@ -1,47 +1,20 @@
1
- import type { ActiveThinkingState, ThinkingUIMode } from "./types.ts";
1
+ import type { ThinkingUIMode } from "./types.ts";
2
2
 
3
3
  const STATE_KEY = Symbol.for("pi-extensions.thinking-ui.state");
4
4
  const DEFAULT_SCOPE_KEY = "__default__";
5
5
  const LABEL_REFRESH_SUFFIX = "\u2060";
6
6
 
7
- type PatchCleanup = () => void | Promise<void>;
8
- type PatchInstallPromise = Promise<PatchCleanup>;
9
- type PatchRelease = () => Promise<void>;
10
-
11
- interface ThinkingActiveEntry {
12
- contentIndex?: number;
13
- }
14
-
15
7
  interface ThinkingUIGlobalState {
16
8
  currentScopeKey: string;
17
9
  modeByScopeKey: Record<string, ThinkingUIMode>;
18
- activeByScopeKey: Record<string, Record<string, ThinkingActiveEntry>>;
19
- lastActiveByScopeKey: Record<string, ActiveThinkingState>;
20
10
  refreshToggleByScope: Record<string, boolean>;
21
- messageScopeByObject: WeakMap<object, string>;
22
- messageObjectsByScope: Record<string, Set<object>>;
23
- messageScopeByTimestamp: Record<string, string>;
24
- patchReleasesByScope: Record<string, PatchRelease[]>;
25
- patchRefCount: number;
26
- patchCleanup?: PatchCleanup | undefined;
27
- patchInstallPromise?: PatchInstallPromise | undefined;
28
11
  }
29
12
 
30
13
  interface LegacyThinkingUIGlobalState {
31
14
  mode?: unknown;
32
- active?: unknown;
33
15
  currentScopeKey?: unknown;
34
16
  modeByScopeKey?: unknown;
35
- activeByScopeKey?: unknown;
36
- lastActiveByScopeKey?: unknown;
37
17
  refreshToggleByScope?: unknown;
38
- messageScopeByObject?: unknown;
39
- messageObjectsByScope?: unknown;
40
- messageScopeByTimestamp?: unknown;
41
- patchReleasesByScope?: unknown;
42
- patchRefCount?: unknown;
43
- patchCleanup?: unknown;
44
- patchInstallPromise?: unknown;
45
18
  }
46
19
 
47
20
  function isRecord(value: unknown): value is Record<PropertyKey, unknown> {
@@ -57,17 +30,6 @@ function normalizeThinkingMode(mode: unknown): ThinkingUIMode {
57
30
  return mode === "collapsed" || mode === "summary" || mode === "expanded" ? mode : "collapsed";
58
31
  }
59
32
 
60
- function normalizeActiveThinkingState(value: unknown): ActiveThinkingState {
61
- if (!isRecord(value) || value.active !== true) {
62
- return { active: false };
63
- }
64
- return {
65
- active: true,
66
- messageTimestamp: typeof value.messageTimestamp === "number" ? value.messageTimestamp : undefined,
67
- contentIndex: typeof value.contentIndex === "number" ? value.contentIndex : undefined,
68
- };
69
- }
70
-
71
33
  function normalizeModeByScopeKey(value: unknown, currentScopeKey: string, legacyMode: unknown): Record<string, ThinkingUIMode> {
72
34
  const modeByScopeKey: Record<string, ThinkingUIMode> = {};
73
35
  if (isRecord(value)) {
@@ -79,84 +41,20 @@ function normalizeModeByScopeKey(value: unknown, currentScopeKey: string, legacy
79
41
  return modeByScopeKey;
80
42
  }
81
43
 
82
- function normalizeActiveByScopeKey(value: unknown): Record<string, Record<string, ThinkingActiveEntry>> {
83
- const activeByScopeKey: Record<string, Record<string, ThinkingActiveEntry>> = {};
84
- if (!isRecord(value)) return activeByScopeKey;
85
- for (const [scopeKey, entries] of Object.entries(value)) {
86
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
87
- activeByScopeKey[normalizedScopeKey] = {};
88
- if (!isRecord(entries)) continue;
89
- for (const [messageTimestamp, entry] of Object.entries(entries)) {
90
- if (!isRecord(entry)) continue;
91
- activeByScopeKey[normalizedScopeKey]![messageTimestamp] = {
92
- contentIndex: typeof entry.contentIndex === "number" ? entry.contentIndex : undefined,
93
- };
94
- }
95
- }
96
- return activeByScopeKey;
97
- }
98
-
99
- function normalizeLastActiveByScopeKey(value: unknown): Record<string, ActiveThinkingState> {
100
- const lastActiveByScopeKey: Record<string, ActiveThinkingState> = {};
101
- if (!isRecord(value)) return lastActiveByScopeKey;
102
- for (const [scopeKey, entry] of Object.entries(value)) {
103
- lastActiveByScopeKey[normalizeThinkingScopeKey(scopeKey)] = normalizeActiveThinkingState(entry);
104
- }
105
- return lastActiveByScopeKey;
106
- }
107
-
108
44
  function ensureGlobalStateShape(state: ThinkingUIGlobalState & LegacyThinkingUIGlobalState): ThinkingUIGlobalState {
109
45
  const currentScopeKey = normalizeThinkingScopeKey(typeof state.currentScopeKey === "string" ? state.currentScopeKey : undefined);
110
46
  const modeByScopeKey = normalizeModeByScopeKey(state.modeByScopeKey, currentScopeKey, state.mode);
111
- const activeByScopeKey = normalizeActiveByScopeKey(state.activeByScopeKey);
112
- const lastActiveByScopeKey = normalizeLastActiveByScopeKey(state.lastActiveByScopeKey);
113
- const legacyActive = normalizeActiveThinkingState(state.active);
114
47
  const refreshToggleByScope: Record<string, boolean> = isRecord(state.refreshToggleByScope)
115
48
  ? Object.fromEntries(Object.entries(state.refreshToggleByScope).map(([scopeKey, enabled]) => [normalizeThinkingScopeKey(scopeKey), enabled === true]))
116
49
  : {};
117
- const messageScopeByObject = state.messageScopeByObject instanceof WeakMap ? state.messageScopeByObject as WeakMap<object, string> : new WeakMap<object, string>();
118
- const messageObjectsByScope: Record<string, Set<object>> = isRecord(state.messageObjectsByScope)
119
- ? Object.fromEntries(Object.entries(state.messageObjectsByScope).map(([scopeKey, messages]) => [normalizeThinkingScopeKey(scopeKey), messages instanceof Set ? messages as Set<object> : new Set<object>()]))
120
- : {};
121
- const messageScopeByTimestamp: Record<string, string> = isRecord(state.messageScopeByTimestamp)
122
- ? Object.fromEntries(Object.entries(state.messageScopeByTimestamp).filter((entry): entry is [string, string] => typeof entry[1] === "string").map(([messageTimestamp, scopeKey]) => [messageTimestamp, normalizeThinkingScopeKey(scopeKey)]))
123
- : {};
124
- const legacyPatchReleasesByScope: Record<string, PatchRelease[]> = isRecord(state.patchReleasesByScope)
125
- ? Object.fromEntries(Object.entries(state.patchReleasesByScope).map(([scopeKey, releases]) => [normalizeThinkingScopeKey(scopeKey), Array.isArray(releases) ? releases as PatchRelease[] : []]))
126
- : {};
127
- const patchReleasesByScope: Record<string, PatchRelease[]> = { ...legacyPatchReleasesByScope };
128
50
 
129
51
  for (const scopeKey of Object.keys(modeByScopeKey)) {
130
- activeByScopeKey[scopeKey] ??= {};
131
- lastActiveByScopeKey[scopeKey] ??= { active: false };
132
52
  refreshToggleByScope[scopeKey] ??= false;
133
- messageObjectsByScope[scopeKey] ??= new Set<object>();
134
- patchReleasesByScope[scopeKey] ??= [];
135
- }
136
-
137
- if (legacyActive.active) {
138
- lastActiveByScopeKey[currentScopeKey] = legacyActive;
139
- if (legacyActive.messageTimestamp !== undefined) {
140
- activeByScopeKey[currentScopeKey]![String(legacyActive.messageTimestamp)] = {
141
- contentIndex: legacyActive.contentIndex,
142
- };
143
- }
144
53
  }
145
54
 
146
55
  state.currentScopeKey = currentScopeKey;
147
56
  state.modeByScopeKey = modeByScopeKey;
148
- state.activeByScopeKey = activeByScopeKey;
149
- state.lastActiveByScopeKey = lastActiveByScopeKey;
150
57
  state.refreshToggleByScope = refreshToggleByScope;
151
- state.messageScopeByObject = messageScopeByObject;
152
- state.messageObjectsByScope = messageObjectsByScope;
153
- state.messageScopeByTimestamp = messageScopeByTimestamp;
154
- state.patchReleasesByScope = patchReleasesByScope;
155
- state.patchRefCount = typeof state.patchRefCount === "number" && Number.isFinite(state.patchRefCount)
156
- ? state.patchRefCount
157
- : 0;
158
- state.patchCleanup = typeof state.patchCleanup === "function" ? state.patchCleanup as PatchCleanup : undefined;
159
- state.patchInstallPromise = state.patchInstallPromise instanceof Promise ? state.patchInstallPromise as PatchInstallPromise : undefined;
160
58
  return state;
161
59
  }
162
60
 
@@ -168,14 +66,7 @@ const globalState = (() => {
168
66
  const created: ThinkingUIGlobalState = {
169
67
  currentScopeKey: DEFAULT_SCOPE_KEY,
170
68
  modeByScopeKey: { [DEFAULT_SCOPE_KEY]: "collapsed" },
171
- activeByScopeKey: { [DEFAULT_SCOPE_KEY]: {} },
172
- lastActiveByScopeKey: { [DEFAULT_SCOPE_KEY]: { active: false } },
173
69
  refreshToggleByScope: {},
174
- messageScopeByObject: new WeakMap<object, string>(),
175
- messageObjectsByScope: { [DEFAULT_SCOPE_KEY]: new Set<object>() },
176
- messageScopeByTimestamp: {},
177
- patchReleasesByScope: {},
178
- patchRefCount: 0,
179
70
  };
180
71
  (globalThis as Record<PropertyKey, unknown>)[STATE_KEY] = created;
181
72
  return created;
@@ -185,21 +76,9 @@ function ensureScopeState(scopeKey: string): void {
185
76
  if (!(scopeKey in globalState.modeByScopeKey)) {
186
77
  globalState.modeByScopeKey[scopeKey] = "collapsed";
187
78
  }
188
- if (!(scopeKey in globalState.activeByScopeKey)) {
189
- globalState.activeByScopeKey[scopeKey] = {};
190
- }
191
- if (!(scopeKey in globalState.lastActiveByScopeKey)) {
192
- globalState.lastActiveByScopeKey[scopeKey] = { active: false };
193
- }
194
79
  if (!(scopeKey in globalState.refreshToggleByScope)) {
195
80
  globalState.refreshToggleByScope[scopeKey] = false;
196
81
  }
197
- if (!(scopeKey in globalState.messageObjectsByScope)) {
198
- globalState.messageObjectsByScope[scopeKey] = new Set<object>();
199
- }
200
- if (!(scopeKey in globalState.patchReleasesByScope)) {
201
- globalState.patchReleasesByScope[scopeKey] = [];
202
- }
203
82
  }
204
83
 
205
84
  export function getCurrentThinkingScopeKey(): string {
@@ -225,62 +104,6 @@ export function setThinkingUIMode(mode: ThinkingUIMode, scopeKey?: string): void
225
104
  globalState.currentScopeKey = normalizedScopeKey;
226
105
  }
227
106
 
228
- export function getActiveThinkingState(messageTimestamp?: number, scopeKey?: string): ActiveThinkingState {
229
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey ?? globalState.currentScopeKey);
230
- ensureScopeState(normalizedScopeKey);
231
-
232
- if (messageTimestamp !== undefined) {
233
- const entry = globalState.activeByScopeKey[normalizedScopeKey]![String(messageTimestamp)];
234
- if (!entry) return { active: false };
235
- return { active: true, messageTimestamp, contentIndex: entry.contentIndex };
236
- }
237
-
238
- return { ...globalState.lastActiveByScopeKey[normalizedScopeKey]! };
239
- }
240
-
241
- export function setActiveThinkingState(state: ActiveThinkingState, scopeKey?: string): void {
242
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey ?? globalState.currentScopeKey);
243
- ensureScopeState(normalizedScopeKey);
244
- globalState.lastActiveByScopeKey[normalizedScopeKey] = { ...state };
245
-
246
- if (!state.active || state.messageTimestamp === undefined) {
247
- if (state.messageTimestamp !== undefined) {
248
- delete globalState.activeByScopeKey[normalizedScopeKey]![String(state.messageTimestamp)];
249
- }
250
- return;
251
- }
252
-
253
- globalState.activeByScopeKey[normalizedScopeKey]![String(state.messageTimestamp)] = {
254
- contentIndex: state.contentIndex,
255
- };
256
- }
257
-
258
- export function clearActiveThinkingState(messageTimestamp?: number, scopeKey?: string): void {
259
- if (messageTimestamp !== undefined) {
260
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey ?? globalState.currentScopeKey);
261
- ensureScopeState(normalizedScopeKey);
262
- delete globalState.activeByScopeKey[normalizedScopeKey]![String(messageTimestamp)];
263
- if (globalState.lastActiveByScopeKey[normalizedScopeKey]!.messageTimestamp === messageTimestamp) {
264
- globalState.lastActiveByScopeKey[normalizedScopeKey] = { active: false };
265
- }
266
- return;
267
- }
268
-
269
- if (scopeKey !== undefined) {
270
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
271
- ensureScopeState(normalizedScopeKey);
272
- globalState.activeByScopeKey[normalizedScopeKey] = {};
273
- globalState.lastActiveByScopeKey[normalizedScopeKey] = { active: false };
274
- return;
275
- }
276
-
277
- for (const existingScopeKey of Object.keys(globalState.modeByScopeKey)) {
278
- ensureScopeState(existingScopeKey);
279
- globalState.activeByScopeKey[existingScopeKey] = {};
280
- globalState.lastActiveByScopeKey[existingScopeKey] = { active: false };
281
- }
282
- }
283
-
284
107
  export function nextThinkingRefreshLabel(label: string, scopeKey?: string): string {
285
108
  const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey ?? globalState.currentScopeKey);
286
109
  ensureScopeState(normalizedScopeKey);
@@ -289,129 +112,16 @@ export function nextThinkingRefreshLabel(label: string, scopeKey?: string): stri
289
112
  return useInvisibleSuffix ? `${label}${LABEL_REFRESH_SUFFIX}` : label;
290
113
  }
291
114
 
292
- export function registerThinkingPatchRelease(scopeKey: string, release: PatchRelease): void {
293
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
294
- ensureScopeState(normalizedScopeKey);
295
- globalState.patchReleasesByScope[normalizedScopeKey]!.push(release);
296
- }
297
-
298
- export function takeThinkingPatchRelease(scopeKey: string): PatchRelease | undefined {
299
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
300
- ensureScopeState(normalizedScopeKey);
301
- return globalState.patchReleasesByScope[normalizedScopeKey]!.pop();
302
- }
303
-
304
- export function recordThinkingMessageScope(message: object, scopeKey?: string): void {
305
- const requestedScopeKey = normalizeThinkingScopeKey(scopeKey ?? globalState.currentScopeKey);
306
- ensureScopeState(requestedScopeKey);
307
-
308
- const existingScopeKey = globalState.messageScopeByObject.get(message);
309
- const normalizedScopeKey = existingScopeKey ?? requestedScopeKey;
310
- ensureScopeState(normalizedScopeKey);
311
- if (!existingScopeKey) {
312
- globalState.messageScopeByObject.set(message, normalizedScopeKey);
313
- }
314
- globalState.messageObjectsByScope[normalizedScopeKey]!.add(message);
315
-
316
- const timestamp = typeof (message as { timestamp?: unknown }).timestamp === "number"
317
- ? (message as { timestamp: number }).timestamp
318
- : undefined;
319
- if (timestamp !== undefined) {
320
- globalState.messageScopeByTimestamp[String(timestamp)] = normalizedScopeKey;
321
- }
322
- }
323
-
324
- export function resolveThinkingMessageScope(message: object, fallbackScopeKey?: string): string {
325
- const objectScopeKey = globalState.messageScopeByObject.get(message);
326
- if (objectScopeKey) {
327
- ensureScopeState(objectScopeKey);
328
- return objectScopeKey;
329
- }
330
-
331
- const timestamp = typeof (message as { timestamp?: unknown }).timestamp === "number"
332
- ? (message as { timestamp: number }).timestamp
333
- : undefined;
334
- if (timestamp !== undefined) {
335
- const timestampScopeKey = globalState.messageScopeByTimestamp[String(timestamp)];
336
- if (timestampScopeKey) {
337
- ensureScopeState(timestampScopeKey);
338
- return timestampScopeKey;
339
- }
340
- }
341
-
342
- const normalizedScopeKey = normalizeThinkingScopeKey(fallbackScopeKey ?? globalState.currentScopeKey);
343
- ensureScopeState(normalizedScopeKey);
344
- return normalizedScopeKey;
345
- }
346
-
347
- export function clearThinkingMessageOwnership(scopeKey?: string): void {
348
- if (scopeKey !== undefined) {
349
- const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
350
- ensureScopeState(normalizedScopeKey);
351
- const ownedMessages = globalState.messageObjectsByScope[normalizedScopeKey] ?? new Set<object>();
352
- for (const message of ownedMessages) {
353
- globalState.messageScopeByObject.delete(message);
354
- }
355
- globalState.messageObjectsByScope[normalizedScopeKey] = new Set<object>();
356
- for (const [messageTimestamp, ownerScopeKey] of Object.entries(globalState.messageScopeByTimestamp)) {
357
- if (ownerScopeKey === normalizedScopeKey) {
358
- delete globalState.messageScopeByTimestamp[messageTimestamp];
359
- }
360
- }
361
- return;
362
- }
363
-
364
- globalState.messageScopeByObject = new WeakMap<object, string>();
365
- globalState.messageObjectsByScope = { [DEFAULT_SCOPE_KEY]: new Set<object>() };
366
- globalState.messageScopeByTimestamp = {};
367
- }
368
-
369
115
  export function resetThinkingUIViewState(scopeKey?: string): void {
370
116
  if (scopeKey !== undefined) {
371
117
  const normalizedScopeKey = normalizeThinkingScopeKey(scopeKey);
372
118
  globalState.currentScopeKey = normalizedScopeKey;
373
119
  globalState.modeByScopeKey[normalizedScopeKey] = "collapsed";
374
120
  globalState.refreshToggleByScope[normalizedScopeKey] = false;
375
- globalState.activeByScopeKey[normalizedScopeKey] = {};
376
- globalState.lastActiveByScopeKey[normalizedScopeKey] = { active: false };
377
- clearThinkingMessageOwnership(normalizedScopeKey);
378
121
  return;
379
122
  }
380
123
 
381
124
  globalState.currentScopeKey = DEFAULT_SCOPE_KEY;
382
125
  globalState.modeByScopeKey = { [DEFAULT_SCOPE_KEY]: "collapsed" };
383
- globalState.activeByScopeKey = { [DEFAULT_SCOPE_KEY]: {} };
384
- globalState.lastActiveByScopeKey = { [DEFAULT_SCOPE_KEY]: { active: false } };
385
126
  globalState.refreshToggleByScope = {};
386
- clearThinkingMessageOwnership();
387
- }
388
-
389
- export function getPatchRefCount(): number {
390
- return globalState.patchRefCount;
391
- }
392
-
393
- export function incrementPatchRefCount(): number {
394
- globalState.patchRefCount += 1;
395
- return globalState.patchRefCount;
396
- }
397
-
398
- export function decrementPatchRefCount(): number {
399
- globalState.patchRefCount = Math.max(0, globalState.patchRefCount - 1);
400
- return globalState.patchRefCount;
401
- }
402
-
403
- export function getPatchCleanup(): PatchCleanup | undefined {
404
- return globalState.patchCleanup;
405
- }
406
-
407
- export function setPatchCleanup(cleanup: PatchCleanup | undefined): void {
408
- globalState.patchCleanup = cleanup;
409
- }
410
-
411
- export function getPatchInstallPromise(): PatchInstallPromise | undefined {
412
- return globalState.patchInstallPromise;
413
- }
414
-
415
- export function setPatchInstallPromise(installPromise: PatchInstallPromise | undefined): void {
416
- globalState.patchInstallPromise = installPromise;
417
127
  }
package/types.ts CHANGED
@@ -51,12 +51,6 @@ export interface DerivedThinkingStep {
51
51
  hasExplicitSuccess?: boolean;
52
52
  }
53
53
 
54
- export interface ActiveThinkingState {
55
- messageTimestamp?: number;
56
- contentIndex?: number;
57
- active: boolean;
58
- }
59
-
60
54
  export interface ThinkingThemeLike {
61
55
  fg(color: string, text: string): string;
62
56
  bold(text: string): string;