@cowwo/dsh-icon-custom 0.15.0 → 0.18.0

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/lib/index.js CHANGED
@@ -40,6 +40,7 @@ import {
40
40
  normalizeUnreadConfig,
41
41
  pruneSeen
42
42
  } from './unread.js'
43
+ import { normalizeRunningConfig } from './running.js'
43
44
 
44
45
  const MAX_BYTES = 10 * 1024 * 1024 // 10 MB
45
46
  // Normalized mime -> safe extension. CUR (mouse cursor) is accepted as ICO.
@@ -338,6 +339,11 @@ export class FaviconCustomService extends TypertRemoteService {
338
339
  // survives a cleared browser profile and means the same thing on every
339
340
  // device, while each browser keeps its own copy as cache + offline queue.
340
341
  this.seenPath = join(this.storageDir, 'unread-seen.json')
342
+ // The "show a yellow mark for every session that is running" switch. Its own
343
+ // file and its own endpoint pair: the running FACT is official (the browser
344
+ // list already carries each session's `running`), so nothing about it is
345
+ // stored here — only whether to show the projection of it.
346
+ this.runningPath = join(this.storageDir, 'running.json')
341
347
  // { activeId, icons: { [id]: { id, mime, ext, name, size, savedAt, rev, pwa, logo } } }
342
348
  this.library = { activeId: null, icons: {} }
343
349
  // The active icon's loaded state: { rev, mime, ext, name, size, savedAt, pwa, logo, bytes } | null
@@ -346,12 +352,16 @@ export class FaviconCustomService extends TypertRemoteService {
346
352
  // Kept in its own file so a settings change can never corrupt the icon
347
353
  // library index.
348
354
  this.unread = normalizeUnreadConfig(null)
355
+ // Running-mark switch: whether the yellow projection of the official
356
+ // `running` fact is drawn. See lib/running.js.
357
+ this.running = normalizeRunningConfig(null)
349
358
  // Host-side watermark: { [sessionId]: endAt } — see lib/unread.js.
350
359
  this.seen = {}
351
360
  // Platform PWA manifest (raw + parsed), read once at startup.
352
361
  this.baseManifest = loadBaseManifest()
353
362
  this._load()
354
363
  this._loadUnread()
364
+ this._loadRunning()
355
365
  this._loadUnreadSeen()
356
366
  // "How did this session's last turn end" as an official session
357
367
  // projection. Optional registration on purpose: this plugin is a display
@@ -450,6 +460,19 @@ export class FaviconCustomService extends TypertRemoteService {
450
460
  return writeFile(this.unreadPath, JSON.stringify(this.unread, null, 2))
451
461
  }
452
462
 
463
+ /** Load the running-mark switch (missing or corrupt file = default: on). */
464
+ _loadRunning() {
465
+ try {
466
+ const raw = JSON.parse(readFileSync(this.runningPath).toString('utf8'))
467
+ this.running = normalizeRunningConfig(raw)
468
+ } catch { /* no persisted switch yet: keep the default */ }
469
+ }
470
+
471
+ /** Persist the running-mark switch (best effort, advisory file). */
472
+ _saveRunning() {
473
+ return writeFile(this.runningPath, JSON.stringify(this.running, null, 2))
474
+ }
475
+
453
476
  /**
454
477
  * Load the host-side watermark.
455
478
  *
@@ -516,6 +539,35 @@ export class FaviconCustomService extends TypertRemoteService {
516
539
  return this._unreadRuleView()
517
540
  }
518
541
 
542
+ /**
543
+ * typert endpoint: read the running-mark switch.
544
+ *
545
+ * Its own endpoint pair, not a field on the unread rule: the two features are
546
+ * independent (a red dot says "something ended and you have not looked", a
547
+ * yellow mark says "something is running right now"), and neither switch may
548
+ * drag the other's storage or response shape along. There is deliberately no
549
+ * `running` field here — who is running is the official host's answer and
550
+ * travels in the ordinary session list.
551
+ * @returns `{ enabled }`.
552
+ */
553
+ async getRunningRule() {
554
+ return { ...this.running }
555
+ }
556
+
557
+ /**
558
+ * typert endpoint: flip the running-mark switch.
559
+ * @param request - `{ enabled: boolean }`; anything else falls back to the default.
560
+ * @returns the stored switch.
561
+ */
562
+ async setRunningRule(request) {
563
+ if (request === null || typeof request !== 'object') {
564
+ throw new Error('缺少 enabled 参数')
565
+ }
566
+ this.running = normalizeRunningConfig(request)
567
+ await this._saveRunning().catch(() => {}) // switch file is advisory
568
+ return { ...this.running }
569
+ }
570
+
519
571
  /**
520
572
  * typert endpoint: merge this browser's "seen" watermarks into the Host's copy.
521
573
  *
package/lib/running.js ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * dsh-icon-custom · 「进行中黄点」的开关(宿主半,纯函数)
3
+ *
4
+ * 这里**没有**任何"谁在跑"的事实,一条也没有——那是官方的:
5
+ * 宿主 `ctx.agents.get(id)?.status === 'running'` 经 `api-session/status`
6
+ * 事件实时推到浏览器,客户端会话列表里本来就带着每个会话的 `running`。
7
+ * 这个插件只把那件事**多投影几次**(黄点/黄数字/黄 favicon 角标)。
8
+ *
9
+ * 所以这个文件只有一个布尔开关,和一条纪律:
10
+ * **不落盘"进行中",只落盘"要不要显示"。** 进程重启后冷会话按官方口径
11
+ * 永远是 `running:false`,不会留下一个永远亮着的假黄点;而开关是偏好,
12
+ * 应该跨设备、跨重启跟着你,所以它归宿主,和红点规则并列但互不牵连
13
+ * (见 docs/adr/0010-running-mark-is-a-second-projection.md)。
14
+ */
15
+
16
+ /** 默认:显示黄点。 */
17
+ export const RUNNING_MARK_DEFAULT = true
18
+
19
+ /**
20
+ * 把任意输入夹成 `{ enabled }`。
21
+ *
22
+ * 只认严格的 `true`/`false`;缺项、脏值、null 一律回落默认——一个读不懂的
23
+ * 开关值不该把一整套标记的可见性变成猜谜。
24
+ * @param input - `{ enabled? }`,可以是脏的、缺项的、null
25
+ * @returns `{ enabled }` —— 永远完整
26
+ */
27
+ export function normalizeRunningConfig(input) {
28
+ const source = input !== null && typeof input === 'object' ? input : {}
29
+ return {
30
+ enabled: typeof source.enabled === 'boolean' ? source.enabled : RUNNING_MARK_DEFAULT
31
+ }
32
+ }
@@ -89,6 +89,21 @@ const unreadSeenSchema = z.object({
89
89
  seen: z.record(z.string(), z.number())
90
90
  })
91
91
 
92
+ /**
93
+ * 进行中黄点的开关。
94
+ *
95
+ * 这里**没有**"谁在跑"的字段,一条都没有:那是官方的 `SessionSummary.running`,
96
+ * 由官方宿主实时推给浏览器,插件只读不存。宿主这一侧只有"要不要显示"这一个偏好,
97
+ * 和红点规则并列、互不牵连(自己的端点对、自己的文件)。
98
+ */
99
+ const runningRuleSchema = z.object({
100
+ enabled: z.boolean()
101
+ })
102
+
103
+ const setRunningRuleRequestSchema = z.object({
104
+ enabled: z.boolean().optional()
105
+ })
106
+
92
107
  const setIconResultSchema = statusSchema
93
108
 
94
109
  export const TYPERT = {
@@ -238,6 +253,45 @@ export const TYPERT = {
238
253
  },
239
254
  sourceLocation: { file: 'lib/index.js', line: 1, column: 1 }
240
255
  },
256
+ {
257
+ id: 'dsh-icon-custom#iconCustom/getRunningRule',
258
+ service: 'iconCustom',
259
+ namespace: 'iconCustom',
260
+ method: 'getRunningRule',
261
+ invocation: { kind: 'direct' },
262
+ parameters: [],
263
+ result: {
264
+ mode: 'strict',
265
+ typeSymbol: 'dsh-icon-custom#iconCustom/getRunningRule:result',
266
+ create: () => runningRuleSchema
267
+ },
268
+ sourceLocation: { file: 'lib/index.js', line: 1, column: 1 }
269
+ },
270
+ {
271
+ id: 'dsh-icon-custom#iconCustom/setRunningRule',
272
+ service: 'iconCustom',
273
+ namespace: 'iconCustom',
274
+ method: 'setRunningRule',
275
+ invocation: { kind: 'direct' },
276
+ parameters: [
277
+ {
278
+ name: 'request',
279
+ wire: 'request',
280
+ source: 'json',
281
+ codec: {
282
+ mode: 'strict',
283
+ typeSymbol: 'dsh-icon-custom#iconCustom/setRunningRule:request',
284
+ create: () => setRunningRuleRequestSchema
285
+ }
286
+ }
287
+ ],
288
+ result: {
289
+ mode: 'strict',
290
+ typeSymbol: 'dsh-icon-custom#iconCustom/setRunningRule:result',
291
+ create: () => runningRuleSchema
292
+ },
293
+ sourceLocation: { file: 'lib/index.js', line: 1, column: 1 }
294
+ },
241
295
  {
242
296
  id: 'dsh-icon-custom#iconCustom/setUnreadSeen',
243
297
  service: 'iconCustom',
@@ -345,6 +399,24 @@ export const TYPERT = {
345
399
  summary: "Merge the seen watermark.",
346
400
  jsDoc: "/** Merge the seen watermark. */",
347
401
  tags: []
402
+ },
403
+ {
404
+ kind: 'method',
405
+ name: 'getRunningRule',
406
+ signature: 'async getRunningRule(): Promise<{ enabled: boolean }>',
407
+ description: "Read the running-mark switch (yellow mark for sessions that are running right now).",
408
+ summary: "Read the running-mark switch.",
409
+ jsDoc: "/** Read the running-mark switch. */",
410
+ tags: []
411
+ },
412
+ {
413
+ kind: 'method',
414
+ name: 'setRunningRule',
415
+ signature: 'async setRunningRule(request: { enabled?: boolean }): Promise<{ enabled: boolean }>',
416
+ description: "Flip the running-mark switch. The running fact itself is official and is never stored here.",
417
+ summary: "Set the running-mark switch.",
418
+ jsDoc: "/** Set the running-mark switch. */",
419
+ tags: []
348
420
  }
349
421
  ],
350
422
  types: []
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@cowwo/dsh-icon-custom",
3
- "version": "0.15.0",
4
- "description": "自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存,自动适配安卓/iOS/平板 PWA 图标,并可把未读数字显示为系统应用图标角标(Windows 任务栏)。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly and persists, auto-sizes PWA icons for Android/iOS/tablets, and projects the unread count onto the system app-icon badge (Windows taskbar).",
3
+ "version": "0.18.0",
4
+ "description": "自定义浏览器标签页图标:在设置页上传 SVG/PNG/ICO,立即生效并持久保存,自动适配安卓/iOS/平板 PWA 图标,显示未读红点与进行中黄点(工作区数字/会话行/左上角/标签页),并可把未读数显示为系统应用图标角标(Windows 任务栏 / macOS Dock,含通知权限申请)。· Custom browser tab favicon: upload SVG/PNG/ICO from the settings page, applies instantly and persists, auto-sizes PWA icons for Android/iOS/tablets, shows unread red dots and running yellow marks (workspace number / session row / top-left / tab icon), and projects the unread count onto the system app-icon badge (Windows taskbar / macOS Dock, notifications permission included).",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "license": "MIT",
8
8
  "scripts": {
9
- "test": "node test/app-badge.mjs && node test/unread-panel.mjs && node test/unread-clear.mjs && node test/unread-rows.mjs && node test/unread-seen.mjs && node test/unread-host-sync.mjs"
9
+ "test": "node test/app-badge.mjs && node test/notification-permission.mjs && node test/unread-panel.mjs && node test/unread-clear.mjs && node test/unread-rows.mjs && node test/unread-seen.mjs && node test/unread-host-sync.mjs && node test/running-mark.mjs"
10
10
  },
11
11
  "dependencies": {
12
12
  "jimp": "^1.6.1",