dsh-plugin-infj-pet 1.0.3 → 1.0.5
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 +66 -0
- package/README.md +22 -15
- package/lib/client.js +35 -22
- package/lib/index.js +13 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,72 @@
|
|
|
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.5
|
|
7
|
+
|
|
8
|
+
**The character and its speech bubble line up**
|
|
9
|
+
|
|
10
|
+
- The box that carries the character was not the size of the character: it
|
|
11
|
+
reserved a ground gap as height while the sprite pushed itself up by the same
|
|
12
|
+
amount as a bottom margin. The visible pixels therefore sat above the box's
|
|
13
|
+
bottom edge, so the sage looked off-centre while the bubble, anchored to the
|
|
14
|
+
box, sat opposite it. The box is now exactly the artwork, and the ground
|
|
15
|
+
clearance is padding on the container — the drawing and the box agree, so the
|
|
16
|
+
speech is centred on the character at every size.
|
|
17
|
+
- The five pixels of horizontal margin that widened the box have gone for the
|
|
18
|
+
same reason.
|
|
19
|
+
|
|
20
|
+
**The bubble is sized to the character**
|
|
21
|
+
|
|
22
|
+
- The bubble was a fixed 250 px at every scale, which crowded a large sage and
|
|
23
|
+
wasted space on a small one. It is now two and a half times the character's own
|
|
24
|
+
width: about 292 px small, 585 px medium, 878 px large, still clamped to the
|
|
25
|
+
viewport so it cannot overflow a narrow window.
|
|
26
|
+
|
|
27
|
+
**Citations name the book**
|
|
28
|
+
|
|
29
|
+
- Quotations are attributed by title only — 《务虚笔记》, 《我与地坛》,
|
|
30
|
+
《病隙碎笔》, 《命若琴弦》 — because the title is what a reader needs in order
|
|
31
|
+
to find the passage. The author's name no longer appears in the interface; it
|
|
32
|
+
remains in the licence notice, where attribution belongs.
|
|
33
|
+
- The reading line supplied by the project owner is now attributed to
|
|
34
|
+
《务虚笔记》, which resolves the source that 1.0.2 had recorded as unidentified.
|
|
35
|
+
The edition is still unpinned and the README says so rather than implying more
|
|
36
|
+
certainty than exists.
|
|
37
|
+
|
|
38
|
+
## 1.0.4
|
|
39
|
+
|
|
40
|
+
**Fix: the plugin never activated**
|
|
41
|
+
|
|
42
|
+
`1.0.3` shipped a Host half that could not start. In Cordis an `inject` entry
|
|
43
|
+
names a service the plugin waits for, and the entry stays pending until that name
|
|
44
|
+
is provided — an object form does not mean "optional". The Host half declared
|
|
45
|
+
`{ optional: ['agents'] }`, which asks for a service literally called `optional`.
|
|
46
|
+
Nothing provides it, so the entry stayed pending forever and the whole plugin,
|
|
47
|
+
browser half included, never loaded. DSH reported it as:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
1 entry did not activate infj-pet (dsh-plugin-infj-pet):
|
|
51
|
+
pending (waiting for service: optional)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The `inject` declaration is gone. The optional agents registry is read with
|
|
55
|
+
`ctx.get('agents')` instead, which returns `undefined` when it is absent and
|
|
56
|
+
costs only the coarse `isSubagent` hint on a recorded boundary.
|
|
57
|
+
|
|
58
|
+
Guards added so this cannot come back:
|
|
59
|
+
|
|
60
|
+
- a test asserts `inject` is `undefined`, with the reason in the failure path
|
|
61
|
+
- a test asserts the Host half still activates and records a boundary when no
|
|
62
|
+
agents registry is present
|
|
63
|
+
- the fake Cordis context no longer needs `agents`, because the plugin must not
|
|
64
|
+
ask for it
|
|
65
|
+
|
|
66
|
+
**A note on why this was missed.** Forty-six of the forty-seven tests exercised
|
|
67
|
+
the browser half, which was never the part failing. The Host half was tested
|
|
68
|
+
against a fake context — and a fake context does not enforce the `inject` gate,
|
|
69
|
+
because that gate is framework behaviour. A complete stub hid a framework-level
|
|
70
|
+
failure; the new assertions target exactly that blind spot.
|
|
71
|
+
|
|
6
72
|
## 1.0.3
|
|
7
73
|
|
|
8
74
|
**The sage no longer paces about**
|
package/README.md
CHANGED
|
@@ -55,23 +55,26 @@ Around that core:
|
|
|
55
55
|
|
|
56
56
|
This is an **in-window** companion, not an operating-system desktop overlay.
|
|
57
57
|
|
|
58
|
-
###
|
|
58
|
+
### Quoted lines
|
|
59
59
|
|
|
60
|
-
Several
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
Several reading lines are short quotations, attributed in the interface under the
|
|
61
|
+
line they belong to — the speech bubble cites the book, and this page records the
|
|
62
|
+
full source. They are brief, individually attributed, and used in a
|
|
63
|
+
non-commercial companion; the project claims no rights in them.
|
|
63
64
|
|
|
64
65
|
| Line | Source |
|
|
65
66
|
|---|---|
|
|
66
|
-
| 如果我进来,走进你独处的时间……我只是来给你的窗上装好玻璃,冬天的风就要来了。 |
|
|
67
|
-
| 死是一件不必急于求成的事。 |
|
|
68
|
-
| 太阳,他每时每刻都是夕阳也都是旭日。 |
|
|
69
|
-
| 我常觉得这中间有着宿命的味道。 |
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
| 如果我进来,走进你独处的时间……我只是来给你的窗上装好玻璃,冬天的风就要来了。 | 《务虚笔记》 — named by the project owner; edition not yet pinned |
|
|
68
|
+
| 死是一件不必急于求成的事。 | 《我与地坛》 |
|
|
69
|
+
| 太阳,他每时每刻都是夕阳也都是旭日。 | 《我与地坛》 |
|
|
70
|
+
| 我常觉得这中间有着宿命的味道。 | 《我与地坛》 |
|
|
71
|
+
|
|
72
|
+
The sage cites the book, not the author's name: the title is what a reader needs
|
|
73
|
+
to find the passage, and it keeps the attribution to a fact rather than a
|
|
74
|
+
byline. Where a line could not be checked against a published edition, this
|
|
75
|
+
project says so — the first row above is recorded that way rather than being
|
|
76
|
+
presented as confirmed. Titles are referenced for the remaining states; no
|
|
77
|
+
passage is reproduced beyond the short lines listed here.
|
|
75
78
|
|
|
76
79
|
---
|
|
77
80
|
|
|
@@ -167,8 +170,12 @@ contract:
|
|
|
167
170
|
- injected shares: `slots`, `sessions`, `connection`, `locale`
|
|
168
171
|
- only `react` and `react-dom/client` are required from the platform module table
|
|
169
172
|
|
|
170
|
-
The Host half declares no
|
|
171
|
-
|
|
173
|
+
The Host half declares **no `inject`**, on purpose. A Cordis `inject` entry names a
|
|
174
|
+
service the plugin waits for, and the entry stays pending until that service is
|
|
175
|
+
provided — so an `inject` that never resolves stops the whole plugin, browser half
|
|
176
|
+
included, from ever activating. The optional agents registry is read with
|
|
177
|
+
`ctx.get('agents')` instead, which returns `undefined` when it is missing and
|
|
178
|
+
costs only the coarse `isSubagent` hint.
|
|
172
179
|
|
|
173
180
|
DSH is evolving quickly. If a future release changes the slot or store contract,
|
|
174
181
|
the sage is written to fail quietly: it falls back to a non-reactive pose rather
|
package/lib/client.js
CHANGED
|
@@ -127,12 +127,12 @@ window.__ModuleLoader__.load({
|
|
|
127
127
|
{
|
|
128
128
|
zh: '死是一件不必急于求成的事。',
|
|
129
129
|
en: 'Dying is a thing that need not be hurried.',
|
|
130
|
-
source: { zh: '
|
|
130
|
+
source: { zh: '《我与地坛》', en: '"The Ditan and I"' }
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
zh: '太阳,他每时每刻都是夕阳也都是旭日。',
|
|
134
134
|
en: 'The sun is, at every moment, both the setting sun and the rising one.',
|
|
135
|
-
source: { zh: '
|
|
135
|
+
source: { zh: '《我与地坛》', en: '"The Ditan and I"' }
|
|
136
136
|
},
|
|
137
137
|
{ zh: '我在,不急。', en: 'I am here. No rush.' }
|
|
138
138
|
],
|
|
@@ -142,7 +142,7 @@ window.__ModuleLoader__.load({
|
|
|
142
142
|
{
|
|
143
143
|
zh: '病隙里也有碎笔。',
|
|
144
144
|
en: 'Fragments get written in the gaps between being ill.',
|
|
145
|
-
source: { zh: '
|
|
145
|
+
source: { zh: '《病隙碎笔》', en: '"Notes Written in the Gaps"' }
|
|
146
146
|
}
|
|
147
147
|
],
|
|
148
148
|
waiting: [
|
|
@@ -154,24 +154,24 @@ window.__ModuleLoader__.load({
|
|
|
154
154
|
{
|
|
155
155
|
zh: '命若琴弦,先别急着弹断它。',
|
|
156
156
|
en: 'Life is like a string on the qin — do not rush to play it through.',
|
|
157
|
-
source: { zh: '
|
|
157
|
+
source: { zh: '《命若琴弦》', en: '"Life Is Like a String"' }
|
|
158
158
|
}
|
|
159
159
|
],
|
|
160
160
|
reading: [
|
|
161
161
|
{
|
|
162
162
|
zh: '如果我进来,走进你独处的时间,你只管你的沉思默想,不不,你不要慌忙起来,对,你想怎样待着就怎样待着,我只是来给你的窗上装好玻璃,冬天的风就要来了。',
|
|
163
163
|
en: 'If I come in, into your private hour — go on with your thinking, no, do not get up in a hurry, yes, stay however you like. I have only come to fit the glass in your window. The winter wind is on its way.',
|
|
164
|
-
source: { zh: '
|
|
164
|
+
source: { zh: '《务虚笔记》', en: '"Notes on a Fiction"' }
|
|
165
165
|
},
|
|
166
166
|
{
|
|
167
167
|
zh: '太阳,他每时每刻都是夕阳也都是旭日。',
|
|
168
168
|
en: 'The sun is, at every moment, both the setting sun and the rising one.',
|
|
169
|
-
source: { zh: '
|
|
169
|
+
source: { zh: '《我与地坛》', en: '"The Ditan and I"' }
|
|
170
170
|
},
|
|
171
171
|
{
|
|
172
172
|
zh: '死是一件不必急于求成的事。',
|
|
173
173
|
en: 'Dying is a thing that need not be hurried.',
|
|
174
|
-
source: { zh: '
|
|
174
|
+
source: { zh: '《我与地坛》', en: '"The Ditan and I"' }
|
|
175
175
|
}
|
|
176
176
|
],
|
|
177
177
|
sleeping: [
|
|
@@ -185,7 +185,7 @@ window.__ModuleLoader__.load({
|
|
|
185
185
|
{
|
|
186
186
|
zh: '我常觉得这中间有着宿命的味道。',
|
|
187
187
|
en: 'I have often felt there was something of fate in all of it.',
|
|
188
|
-
source: { zh: '
|
|
188
|
+
source: { zh: '《我与地坛》', en: '"The Ditan and I"' }
|
|
189
189
|
}
|
|
190
190
|
]
|
|
191
191
|
};
|
|
@@ -454,18 +454,20 @@ window.__ModuleLoader__.load({
|
|
|
454
454
|
|
|
455
455
|
var scale = opts.scale || 1;
|
|
456
456
|
var art = SP.stage.art;
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
//
|
|
460
|
-
//
|
|
461
|
-
// of the source pixels so nothing is
|
|
457
|
+
// The box is exactly the artwork, with no slop: any margin here would sit
|
|
458
|
+
// between the visible pixels and the box edge, and the bubble is anchored to
|
|
459
|
+
// the box, so the character would look offset from its own speech. The sheet's
|
|
460
|
+
// empty cell margin is already trimmed by using the measured art box, and
|
|
461
|
+
// everything stays an integer multiple of the source pixels so nothing is
|
|
462
|
+
// resampled. The little clearance under the feet is CSS padding on the
|
|
463
|
+
// container, which keeps the layout and the drawing in agreement.
|
|
462
464
|
return {
|
|
463
|
-
width:
|
|
464
|
-
height:
|
|
465
|
+
width: art.width * scale,
|
|
466
|
+
height: art.height * scale,
|
|
465
467
|
backgroundSize: SP.sheet.width * scale + 'px ' + SP.sheet.height * scale + 'px',
|
|
466
468
|
backgroundPosition: -Math.round(cell[0] * scale) + 'px ' + -Math.round(cell[1] * scale) + 'px',
|
|
467
|
-
//
|
|
468
|
-
|
|
469
|
+
// The ground line is the artwork's last row, so nothing hangs below it.
|
|
470
|
+
groundGap: (SP.stage.ground - (art.y + art.height)) * scale
|
|
469
471
|
};
|
|
470
472
|
}
|
|
471
473
|
|
|
@@ -712,7 +714,11 @@ window.__ModuleLoader__.load({
|
|
|
712
714
|
'.infj-sprite{image-rendering:pixelated;image-rendering:crisp-edges;background-repeat:no-repeat;',
|
|
713
715
|
'transform-origin:bottom center;transition:transform .16s ease-out}',
|
|
714
716
|
'.infj-pet.is-poked .infj-sprite{transform:scale(1.06)}',
|
|
715
|
-
|
|
717
|
+
// The bubble is anchored to the box, so its width is derived from the box
|
|
718
|
+
// rather than fixed: a larger sage gets a proportionally larger bubble, and
|
|
719
|
+
// the wrap stays comfortable at every size.
|
|
720
|
+
'.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);',
|
|
716
722
|
'min-width:72px;padding:8px 12px;border-radius:14px;background:var(--s-panel);color:var(--s-ink);',
|
|
717
723
|
'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;',
|
|
718
724
|
'border:1px solid var(--s-chip);pointer-events:none;animation:infj-bubble-in .22s ease-out;text-align:center;',
|
|
@@ -1002,8 +1008,11 @@ window.__ModuleLoader__.load({
|
|
|
1002
1008
|
}) || SIZES[1];
|
|
1003
1009
|
var scale = size.scale;
|
|
1004
1010
|
var art = SP.stage.art;
|
|
1005
|
-
|
|
1006
|
-
|
|
1011
|
+
// The box is the artwork itself; the sheet's own ground gap becomes padding
|
|
1012
|
+
// below it so the character is centred both ways inside its own box.
|
|
1013
|
+
var groundGap = (SP.stage.ground - (art.y + art.height)) * scale;
|
|
1014
|
+
var boxWidth = art.width * scale;
|
|
1015
|
+
var boxHeight = art.height * scale + groundGap;
|
|
1007
1016
|
|
|
1008
1017
|
var rootRef = React.useRef(null);
|
|
1009
1018
|
var dragRef = React.useRef(null);
|
|
@@ -1284,6 +1293,11 @@ window.__ModuleLoader__.load({
|
|
|
1284
1293
|
var style = {
|
|
1285
1294
|
width: boxWidth + 'px',
|
|
1286
1295
|
height: boxHeight + 'px',
|
|
1296
|
+
// The bubble derives its width from the character's own size, so a
|
|
1297
|
+
// larger sage gets a proportionally larger bubble instead of the same
|
|
1298
|
+
// cramped box at every scale.
|
|
1299
|
+
'--s-bubble': boxWidth + 'px',
|
|
1300
|
+
paddingBottom: groundGap + 'px',
|
|
1287
1301
|
left: position.x == null ? undefined : position.x + 'px',
|
|
1288
1302
|
top: position.y == null ? undefined : position.y + 'px',
|
|
1289
1303
|
right: position.x == null ? '24px' : undefined,
|
|
@@ -1344,8 +1358,7 @@ window.__ModuleLoader__.load({
|
|
|
1344
1358
|
backgroundSize: spec.backgroundSize,
|
|
1345
1359
|
backgroundPosition: spec.backgroundPosition,
|
|
1346
1360
|
width: spec.width + 'px',
|
|
1347
|
-
height: spec.height + 'px'
|
|
1348
|
-
marginBottom: spec.bottomInset + 'px'
|
|
1361
|
+
height: spec.height + 'px'
|
|
1349
1362
|
}
|
|
1350
1363
|
})
|
|
1351
1364
|
);
|
package/lib/index.js
CHANGED
|
@@ -15,8 +15,19 @@
|
|
|
15
15
|
/** Cordis plugin name; also the service key. */
|
|
16
16
|
export const name = 'infjPet';
|
|
17
17
|
|
|
18
|
-
/**
|
|
19
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Deliberately no `inject`.
|
|
20
|
+
*
|
|
21
|
+
* A Cordis `inject` entry names a service the plugin waits for, and the plugin
|
|
22
|
+
* stays pending until that name is provided. An object form does not mean
|
|
23
|
+
* "optional": `{ optional: ['agents'] }` asks for a service literally called
|
|
24
|
+
* `optional`, which never arrives, so the whole entry never activates.
|
|
25
|
+
*
|
|
26
|
+
* The agents registry is read with `ctx.get('agents')` in `apply` instead. That
|
|
27
|
+
* returns `undefined` when the service is absent, which is exactly the
|
|
28
|
+
* degradation wanted here: boundaries are still recorded, only the coarse
|
|
29
|
+
* `isSubagent` hint is left off.
|
|
30
|
+
*/
|
|
20
31
|
|
|
21
32
|
/** Bounded ring of remembered boundaries. */
|
|
22
33
|
export const HISTORY_LIMIT = 256;
|
package/package.json
CHANGED