dsh-plugin-infj-pet 1.0.5 → 1.0.9

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 CHANGED
@@ -3,6 +3,59 @@
3
3
  All notable changes to this plugin are documented here. This project follows
4
4
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
5
5
 
6
+ ## 1.0.9
7
+
8
+ **Fix: the character was drawn cropped**
9
+
10
+ The element frames the artwork, but the background offset subtracted only the
11
+ frame origin and not where the artwork sits inside its cell. Every frame was
12
+ therefore drawn `art.x` too far right — 37 source pixels, 74 at 2× — and the
13
+ excess fell outside the element. At the default size roughly half the character
14
+ was cut off.
15
+
16
+ The bug had been there since the sheet was adopted. The original 12 px margin on
17
+ the element absorbed exactly that much of it, so it read as a slightly off-centre
18
+ character rather than a broken one; removing the margin in 1.0.5 to fix the
19
+ alignment exposed it.
20
+
21
+ A test now checks every frame of every animation against the element that draws
22
+ it, so a frame that would be cropped fails the suite rather than reaching a
23
+ release.
24
+
25
+ ## 1.0.8
26
+
27
+ - The default resting place is right-anchored again, 96px from the right and 40px
28
+ up from the bottom. 1.0.7's top-left corner put the sage under the shell's left
29
+ sidebar, which is about 264px wide, so only its right-hand pixels showed.
30
+ - The reason is worth recording: the companion is positioned against the
31
+ **viewport**, so it cannot see the shell's layout. A left inset lands under the
32
+ sidebar and a top inset under the window's title strip; the right edge is the
33
+ one side that is always clear.
34
+ - The layout version moved to 3, so the corrected default reaches anyone who had
35
+ already dragged the sage out of the 1.0.7 corner.
36
+
37
+ ## 1.0.7
38
+
39
+ - The sage rests in the **top-left** corner. It floats above the shell, so it
40
+ overlaps the sidebar rather than being hidden behind it.
41
+ - A changed default position now reaches people who have dragged the sage before.
42
+ A remembered position beats the default, which is right while someone is
43
+ arranging their own desktop — but it also meant a corrected default was never
44
+ seen. The saved record now carries the layout it belongs to, and a position
45
+ saved under an older layout is ignored once.
46
+ - Two drag tests were anchored to the old bottom-right start and are rewritten to
47
+ derive from the viewport, so a future default change will not need them edited.
48
+
49
+ ## 1.0.6
50
+
51
+ - The resting position sits further from the right edge. Removing the box's
52
+ internal slack in 1.0.5 left the sage flush against the window edge, which read
53
+ as too far right; the corner inset is now wider than the bottom's.
54
+ - The speech bubble is a quarter shorter — 1.875× the character's width instead
55
+ of 2.5×, so about 219 px small, 439 px medium and 658 px large. The 1.0.5
56
+ widening overshot; this keeps the comfortable wrap without the long measure.
57
+ - The README is the project owner's revision.
58
+
6
59
  ## 1.0.5
7
60
 
8
61
  **The character and its speech bubble line up**
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # INFJ Sage · 贤者桌宠
1
+ # INFJ Sage · 老绿
2
2
 
3
- **A pixel-art INFJ companion that lives inside your DeepSeek Harness window.**
3
+ **A pixel-art INFJ-A companion that lives inside your DeepSeek Harness window.**
4
4
 
5
5
  **Just here to accompany Qiangwei.**
6
6
 
7
- 一只住在 DSH 窗口里的 INFJ 贤者:陪你专注、等你决定,也陪你安静地打个盹。
7
+ 这是绿老头,不是brother:陪你专注、等你决定,也陪你安静地打个盹。
8
8
 
9
9
  <p align="center">
10
10
  <img src="assets/icon.png" width="120" alt="INFJ Sage icon">
@@ -23,35 +23,35 @@ card.
23
23
  While you work it sits and reads, turning its pages. It only runs when you pick
24
24
  it up.
25
25
 
26
- ---
26
+ \---
27
27
 
28
28
  ## What it does
29
29
 
30
- | Pose | When it appears | Trait it expresses |
31
- |---|---|---|
32
- | 静观 Observing | Nothing is running | 独处即充电 — recharged alone |
33
- | 阅读 Reading | At least one session is working, or a watched turn just finished | 先看模式,再看任务 — patterns before tasks |
34
- | 共情 Empathy | A session is waiting on your answer | 先接住情绪 — feelings first |
35
- | 充电 Recharging | Idle past the nap delay | 独处即充电 — the quiet half |
36
- | 受挫 Setback | A turn ended in an error | 过载时向内自责 — overload turns inward |
37
- | 带我去哪儿 Carried | While you are dragging it | 理想主义的续航 — carried, not chasing |
30
+ |Pose|When it appears|Trait it expresses|
31
+ |-|-|-|
32
+ |静观 Observing|Nothing is running|独处即充电 — recharged alone|
33
+ |阅读 Reading|At least one session is working, or a watched turn just finished|先看模式,再看任务 — patterns before tasks|
34
+ |共情 Empathy|A session is waiting on your answer|先接住情绪 — feelings first|
35
+ |充电 Recharging|Idle past the nap delay|独处即充电 — the quiet half|
36
+ |受挫 Setback|A turn ended in an error|过载时向内自责 — overload turns inward|
37
+ |带我去哪儿 Carried|While you are dragging it|理想主义的续航 — carried, not chasing|
38
38
 
39
39
  Around that core:
40
40
 
41
- - **Click** the sage and it says something in character.
42
- - **Drag** it anywhere; it snaps to the edge and remembers where you left it.
43
- - **It reads while you work** — an open book, eyes following the page, a quiet
44
- page turn. It never paces about on its own.
45
- - **It runs only when carried** — pick it up and it runs in the direction you
46
- take it, turning around at the screen edge so it never looks like it is
47
- running out of the window.
48
- - **Right-click** for settings: session scope, size, nap delay, palette, motion,
49
- idle glances, auto-tips, position reset, tuck away, and a trait panel.
50
- - **Idle glances** — while resting, the sage looks around using the sheet's
51
- sixteen drawn head poses.
52
- - Follows DSH's language, light/dark theme, and reduce-motion preference.
53
- - No build step to install, no runtime dependencies, no network calls, no model
54
- calls.
41
+ * **Click** the sage and it says something in character.
42
+ * **Drag** it anywhere; it snaps to the edge and remembers where you left it.
43
+ * **It reads while you work** — an open book, eyes following the page, a quiet
44
+ page turn. It never paces about on its own.
45
+ * **It runs only when carried** — pick it up and it runs in the direction you
46
+ take it, turning around at the screen edge so it never looks like it is
47
+ running out of the window.
48
+ * **Right-click** for settings: session scope, size, nap delay, palette, motion,
49
+ idle glances, auto-tips, position reset, tuck away, and a trait panel.
50
+ * **Idle glances** — while resting, the sage looks around using the sheet's
51
+ sixteen drawn head poses.
52
+ * Follows DSH's language, light/dark theme, and reduce-motion preference.
53
+ * No build step to install, no runtime dependencies, no network calls, no model
54
+ calls.
55
55
 
56
56
  This is an **in-window** companion, not an operating-system desktop overlay.
57
57
 
@@ -62,12 +62,12 @@ line they belong to — the speech bubble cites the book, and this page records
62
62
  full source. They are brief, individually attributed, and used in a
63
63
  non-commercial companion; the project claims no rights in them.
64
64
 
65
- | Line | Source |
66
- |---|---|
67
- | 如果我进来,走进你独处的时间……我只是来给你的窗上装好玻璃,冬天的风就要来了。 | 《务虚笔记》 — named by the project owner; edition not yet pinned |
68
- | 死是一件不必急于求成的事。 | 《我与地坛》 |
69
- | 太阳,他每时每刻都是夕阳也都是旭日。 | 《我与地坛》 |
70
- | 我常觉得这中间有着宿命的味道。 | 《我与地坛》 |
65
+ |Line|Source|
66
+ |-|-|
67
+ |如果我进来,走进你独处的时间……我只是来给你的窗上装好玻璃,冬天的风就要来了。|《务虚笔记》 — named by the project owner; edition not yet pinned|
68
+ |死是一件不必急于求成的事。|《我与地坛》|
69
+ |太阳,他每时每刻都是夕阳也都是旭日。|《我与地坛》|
70
+ |我常觉得这中间有着宿命的味道。|《我与地坛》|
71
71
 
72
72
  The sage cites the book, not the author's name: the title is what a reader needs
73
73
  to find the passage, and it keeps the attribution to a fact rather than a
@@ -76,11 +76,11 @@ project says so — the first row above is recorded that way rather than being
76
76
  presented as confirmed. Titles are referenced for the remaining states; no
77
77
  passage is reproduced beyond the short lines listed here.
78
78
 
79
- ---
79
+ \---
80
80
 
81
81
  ## Install
82
82
 
83
- ### 1. From npm
83
+ ### 1\. From npm
84
84
 
85
85
  Package page: [dsh-plugin-infj-pet on npm](https://www.npmjs.com/package/dsh-plugin-infj-pet).
86
86
 
@@ -92,22 +92,22 @@ dsh-plugin-infj-pet
92
92
 
93
93
  To pin this release, use `dsh-plugin-infj-pet@1.0.1`.
94
94
 
95
- ### 2. From GitHub
95
+ ### 2\. From GitHub
96
96
 
97
97
  ```
98
- github:CN-Mg/dsh_infj_pet
98
+ github:CN-Mg/dsh\_infj\_pet
99
99
  ```
100
100
 
101
101
  Or with a pinned tag:
102
102
 
103
103
  ```
104
- github:CN-Mg/dsh_infj_pet#v1.0.1
104
+ github:CN-Mg/dsh\_infj\_pet#v1.0.1
105
105
  ```
106
106
 
107
107
  The committed source already contains the built client bundle, so a Git install
108
108
  needs no build step.
109
109
 
110
- ### 3. From a local folder
110
+ ### 3\. From a local folder
111
111
 
112
112
  Point the installer at the absolute path of this directory, or pack it first:
113
113
 
@@ -120,21 +120,21 @@ may not quit the application, and the browser bundle is only picked up on boot.
120
120
 
121
121
  To remove it: disable the row on the Plugins page, then uninstall the package.
122
122
 
123
- ---
123
+ \---
124
124
 
125
125
  ## Using it
126
126
 
127
- - **Click** — a line of dialogue, different each time.
128
- - **Drag** — reposition; release near a side and it snaps to that edge.
129
- - **Right-click** — settings and pose preview.
130
- - **Keyboard** — `Tab` to the sage, `Enter`/`Space` to poke, `Esc` to close.
131
- - **Session scope** — *All sessions* counts ordinary sessions on the current
132
- Host; *This session* follows only the one you are looking at. Child (subagent)
133
- sessions are never counted as your sessions, so a delegated burst does not look
134
- like your own work.
135
- - **Nap delay** — how long the sage stays quiet before it dozes off.
136
- - **Size** — 1×, 2×, or 3× the sprite's own pixels. Every size is a whole-number
137
- multiple, because pixel art resampled at a fractional scale turns to mush.
127
+ * **Click** — a line of dialogue, different each time.
128
+ * **Drag** — reposition; release near a side and it snaps to that edge.
129
+ * **Right-click** — settings and pose preview.
130
+ * **Keyboard** — `Tab` to the sage, `Enter`/`Space` to poke, `Esc` to close.
131
+ * **Session scope** — *All sessions* counts ordinary sessions on the current
132
+ Host; *This session* follows only the one you are looking at. Child (subagent)
133
+ sessions are never counted as your sessions, so a delegated burst does not look
134
+ like your own work.
135
+ * **Nap delay** — how long the sage stays quiet before it dozes off.
136
+ * **Size** — 1×, 2×, or 3× the sprite's own pixels. Every size is a whole-number
137
+ multiple, because pixel art resampled at a fractional scale turns to mush.
138
138
 
139
139
  ### Completion is never guessed
140
140
 
@@ -143,32 +143,32 @@ watched running**. A page that loads into an already-idle Host, a dropped
143
143
  connection, or a session that simply disappears never produces a celebration.
144
144
  Cancelled and failed turns are not successes.
145
145
 
146
- ---
146
+ \---
147
147
 
148
148
  ## Privacy and runtime boundaries
149
149
 
150
- - Reads session **identity and status** only — never message content, tool
151
- arguments, or transcripts.
152
- - Makes **no model calls**, opens **no ports**, sends **no telemetry**, and
153
- answers **no approvals** on your behalf.
154
- - Uses DSH's existing authenticated connection.
155
- - The sprite sheet is embedded in the bundle, so the plugin issues **no network
156
- request at all**.
157
- - Preferences live in `localStorage` under `dsh-plugin-infj-pet:v1`.
158
- - Writes only to its own DOM nodes and to one `<style>` element it owns.
159
- - Does not modify DSH itself, and replaces no official component.
150
+ * Reads session **identity and status** only — never message content, tool
151
+ arguments, or transcripts.
152
+ * Makes **no model calls**, opens **no ports**, sends **no telemetry**, and
153
+ answers **no approvals** on your behalf.
154
+ * Uses DSH's existing authenticated connection.
155
+ * The sprite sheet is embedded in the bundle, so the plugin issues **no network
156
+ request at all**.
157
+ * Preferences live in `localStorage` under `dsh-plugin-infj-pet:v1`.
158
+ * Writes only to its own DOM nodes and to one `<style>` element it owns.
159
+ * Does not modify DSH itself, and replaces no official component.
160
160
 
161
- ---
161
+ \---
162
162
 
163
163
  ## Compatibility
164
164
 
165
165
  Developed against **DSH Desktop 0.2.x** using the documented client plugin
166
166
  contract:
167
167
 
168
- - client bundle registered through `window.__ModuleLoader__.load({ id, factory })`
169
- - one `shell.overlay` slot occupant (`id: infj-pet`, `order: 92`)
170
- - injected shares: `slots`, `sessions`, `connection`, `locale`
171
- - only `react` and `react-dom/client` are required from the platform module table
168
+ * client bundle registered through `window.\_\_ModuleLoader\_\_.load({ id, factory })`
169
+ * one `shell.overlay` slot occupant (`id: infj-pet`, `order: 92`)
170
+ * injected shares: `slots`, `sessions`, `connection`, `locale`
171
+ * only `react` and `react-dom/client` are required from the platform module table
172
172
 
173
173
  The Host half declares **no `inject`**, on purpose. A Cordis `inject` entry names a
174
174
  service the plugin waits for, and the entry stays pending until that service is
@@ -181,7 +181,7 @@ DSH is evolving quickly. If a future release changes the slot or store contract,
181
181
  the sage is written to fail quietly: it falls back to a non-reactive pose rather
182
182
  than throwing into your session.
183
183
 
184
- ---
184
+ \---
185
185
 
186
186
  ## Development
187
187
 
@@ -256,36 +256,37 @@ tools/ generators, checker, preview builder, PNG reader
256
256
  tests/ Node test-runner suite with a small React harness
257
257
  ```
258
258
 
259
- `tools/sage_contact.py` and `tools/sage_measure.py` rasterise the sheet for
259
+ `tools/sage\_contact.py` and `tools/sage\_measure.py` rasterise the sheet for
260
260
  review outside DSH; they need Pillow and are development aids only.
261
261
 
262
262
  ### Design notes
263
263
 
264
- - **Fail soft.** A missing slot registry falls back to a direct DOM mount; a
265
- store hook that throws leaves a static but present companion.
266
- - **Never invent success.** The machine requires an observed rising edge before a
267
- falling edge can celebrate.
268
- - **One artwork source.** Frames, gaze cells, the icon and the bundle are all
269
- derived from the sheet, and every derivation is re-checked in CI.
270
- - **Integer pixels only.** Every frame offset and scale factor is a whole number,
271
- asserted by a test, because that is what keeps the drawing crisp.
272
- - **Own your nodes.** No global mutation beyond the plugin's own style element
273
- and its namespaced storage key.
264
+ * **Fail soft.** A missing slot registry falls back to a direct DOM mount; a
265
+ store hook that throws leaves a static but present companion.
266
+ * **Never invent success.** The machine requires an observed rising edge before a
267
+ falling edge can celebrate.
268
+ * **One artwork source.** Frames, gaze cells, the icon and the bundle are all
269
+ derived from the sheet, and every derivation is re-checked in CI.
270
+ * **Integer pixels only.** Every frame offset and scale factor is a whole number,
271
+ asserted by a test, because that is what keeps the drawing crisp.
272
+ * **Own your nodes.** No global mutation beyond the plugin's own style element
273
+ and its namespaced storage key.
274
274
 
275
- ---
275
+ \---
276
276
 
277
277
  ## Credits and licenses
278
278
 
279
- - **Code:** [MIT](LICENSE).
280
- - **Artwork:** [CC BY-NC-SA 4.0](ASSETS-LICENSE.md) — share and adapt
281
- non-commercially with attribution; separate from the code license.
282
- - The character was generated with a built-in image generator from the supplied
283
- INFJ reference card; `Sage/README.md` records the pet's provenance and stable
284
- id, and `Sage/prompts/` holds the production prompts.
285
- - No fonts are bundled; all text uses the platform UI font stack.
286
- - INFJ and *Advocate* / *Counselor* are popular-psychology vocabulary, not a
287
- clinical instrument. This plugin is decorative, assesses no one, and is
288
- affiliated with no personality-assessment publisher.
279
+ * **Code:** [MIT](LICENSE).
280
+ * **Artwork:** [CC BY-NC-SA 4.0](ASSETS-LICENSE.md) — share and adapt
281
+ non-commercially with attribution; separate from the code license.
282
+ * The character was generated with a built-in image generator from the supplied
283
+ INFJ reference card; `Sage/README.md` records the pet's provenance and stable
284
+ id, and `Sage/prompts/` holds the production prompts.
285
+ * No fonts are bundled; all text uses the platform UI font stack.
286
+ * INFJ and *Advocate* / *Counselor* are popular-psychology vocabulary, not a
287
+ clinical instrument. This plugin is decorative, assesses no one, and is
288
+ affiliated with no personality-assessment publisher.
289
289
 
290
290
  This is a personal project, not an official DeepSeek product. It does not
291
291
  represent DeepSeek's views or endorsement.
292
+
package/lib/client.js CHANGED
@@ -35,6 +35,17 @@ window.__ModuleLoader__.load({
35
35
  * ------------------------------------------------------------------ */
36
36
 
37
37
  var STORE_KEY = 'dsh-plugin-infj-pet:v1';
38
+
39
+ /**
40
+ * Bumped whenever the default resting place changes.
41
+ *
42
+ * A remembered position always wins over the default, which is right while the
43
+ * person is arranging their own desktop — but it also means a corrected
44
+ * default would never reach anyone who had ever dragged the sage. Recording
45
+ * the layout version alongside the position lets a default change take effect
46
+ * once, without discarding the position on every reload.
47
+ */
48
+ var LAYOUT_VERSION = 3;
38
49
  var STYLE_ID = 'dsh-plugin-infj-pet-styles';
39
50
  var EDGE_SNAP = 18;
40
51
 
@@ -461,11 +472,17 @@ window.__ModuleLoader__.load({
461
472
  // everything stays an integer multiple of the source pixels so nothing is
462
473
  // resampled. The little clearance under the feet is CSS padding on the
463
474
  // container, which keeps the layout and the drawing in agreement.
475
+ //
476
+ // The background must be positioned by the artwork's origin, not the frame's:
477
+ // the artwork sits art.x/art.y into its cell, and the element shows only
478
+ // art.width x art.height. Subtracting the frame origin alone draws every
479
+ // frame art.x too far right and crops the excess off the element.
464
480
  return {
465
481
  width: art.width * scale,
466
482
  height: art.height * scale,
467
483
  backgroundSize: SP.sheet.width * scale + 'px ' + SP.sheet.height * scale + 'px',
468
- backgroundPosition: -Math.round(cell[0] * scale) + 'px ' + -Math.round(cell[1] * scale) + 'px',
484
+ backgroundPosition:
485
+ -Math.round((cell[0] + art.x) * scale) + 'px ' + -Math.round((cell[1] + art.y) * scale) + 'px',
469
486
  // The ground line is the artwork's last row, so nothing hangs below it.
470
487
  groundGap: (SP.stage.ground - (art.y + art.height)) * scale
471
488
  };
@@ -718,7 +735,7 @@ window.__ModuleLoader__.load({
718
735
  // rather than fixed: a larger sage gets a proportionally larger bubble, and
719
736
  // the wrap stays comfortable at every size.
720
737
  '.infj-bubble{position:absolute;bottom:calc(100% + 6px);left:50%;transform:translateX(-50%);',
721
- 'box-sizing:border-box;width:calc(var(--s-bubble) * 2.5);max-width:min(88vw,var(--s-bubble) * 2.5);',
738
+ 'box-sizing:border-box;width:calc(var(--s-bubble) * 1.875);max-width:min(88vw,var(--s-bubble) * 1.875);',
722
739
  'min-width:72px;padding:8px 12px;border-radius:14px;background:var(--s-panel);color:var(--s-ink);',
723
740
  'font-size:12.5px;font-weight:500;box-shadow:0 6px 22px rgba(16,34,16,0.18),0 1px 0 rgba(255,255,255,0.5) inset;',
724
741
  'border:1px solid var(--s-chip);pointer-events:none;animation:infj-bubble-in .22s ease-out;text-align:center;',
@@ -855,8 +872,16 @@ window.__ModuleLoader__.load({
855
872
  motion: saved.motion === 'on' || saved.motion === 'off' ? saved.motion : 'auto',
856
873
  hidden: saved.hidden === true,
857
874
  theme: saved.theme === 'light' || saved.theme === 'dark' ? saved.theme : null,
858
- x: typeof saved.x === 'number' && Number.isFinite(saved.x) ? saved.x : null,
859
- y: typeof saved.y === 'number' && Number.isFinite(saved.y) ? saved.y : null,
875
+ // A position saved under an older layout is ignored once, so a corrected
876
+ // default is actually seen.
877
+ x:
878
+ saved.layoutVersion === LAYOUT_VERSION && typeof saved.x === 'number' && Number.isFinite(saved.x)
879
+ ? saved.x
880
+ : null,
881
+ y:
882
+ saved.layoutVersion === LAYOUT_VERSION && typeof saved.y === 'number' && Number.isFinite(saved.y)
883
+ ? saved.y
884
+ : null,
860
885
  language: normalizeLanguage(saved.language)
861
886
  };
862
887
  }
@@ -1241,7 +1266,7 @@ window.__ModuleLoader__.load({
1241
1266
  y: Math.round(clamp(box.y, 0, limit.maxY))
1242
1267
  };
1243
1268
  setPosition(next);
1244
- store.write({ x: next.x, y: next.y });
1269
+ store.write({ x: next.x, y: next.y, layoutVersion: LAYOUT_VERSION });
1245
1270
  }
1246
1271
 
1247
1272
  function onContextMenu(event) {
@@ -1298,10 +1323,15 @@ window.__ModuleLoader__.load({
1298
1323
  // cramped box at every scale.
1299
1324
  '--s-bubble': boxWidth + 'px',
1300
1325
  paddingBottom: groundGap + 'px',
1326
+ // Anchored right, and up from the bottom. The companion is positioned
1327
+ // against the viewport, so it cannot see the shell's layout: a left inset
1328
+ // puts it under the sidebar (about 264px wide) and a top inset under the
1329
+ // title strip. The right edge is the one side that is always free, and
1330
+ // the insets keep it off the corner itself.
1301
1331
  left: position.x == null ? undefined : position.x + 'px',
1302
1332
  top: position.y == null ? undefined : position.y + 'px',
1303
- right: position.x == null ? '24px' : undefined,
1304
- bottom: position.x == null ? '24px' : undefined
1333
+ right: position.x == null ? '96px' : undefined,
1334
+ bottom: position.x == null ? '40px' : undefined
1305
1335
  };
1306
1336
 
1307
1337
  var children = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-infj-pet",
3
- "version": "1.0.5",
3
+ "version": "1.0.9",
4
4
  "description": "INFJ Sage — a pixel-art Advocate/Counselor companion that lives in your DSH window and reflects what your agents are doing.",
5
5
  "keywords": [
6
6
  "dsh",