@linxin666/dsh-pet 0.3.4 → 0.3.6
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/README.i18n.yaml +2 -2
- package/README.md +13 -10
- package/README.zh.md +13 -10
- package/contracts/voice-pack-v1.schema.json +53 -28
- package/cordis.patch.yml +1 -1
- package/lib/client.js +166 -30
- package/lib/client.js.map +1 -1
- package/lib/index.js +400 -478
- package/lib/invariant.js +1 -1
- package/lib/{state-DrMX22GL.js → state-NvDEoCln.js} +5 -2
- package/lib/types/access.d.ts +5 -4
- package/lib/types/access.d.ts.map +1 -1
- package/lib/types/access.js +2 -12
- package/lib/types/affinity.d.ts +2 -0
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +12 -0
- package/lib/types/chatter.d.ts +68 -46
- package/lib/types/chatter.d.ts.map +1 -1
- package/lib/types/chatter.js +243 -301
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +23 -18
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +26 -9
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +18 -0
- package/lib/types/client/renderers/frames2d.d.ts +12 -0
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
- package/lib/types/client/renderers/frames2d.js +143 -18
- package/lib/types/event-projection.d.ts +9 -4
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +46 -14
- package/lib/types/ledger.d.ts +4 -2
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +4 -4
- package/lib/types/pair-access.d.ts +35 -0
- package/lib/types/pair-access.d.ts.map +1 -0
- package/lib/types/pair-access.js +19 -0
- package/lib/types/remarks.d.ts +1 -1
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/remarks.js +11 -2
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +8 -3
- package/lib/types/service.d.ts +7 -11
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +31 -20
- package/lib/types/voice-pack.d.ts +11 -7
- package/lib/types/voice-pack.d.ts.map +1 -1
- package/lib/types/voice-pack.js +80 -49
- package/package.json +1 -1
- package/src/access.ts +7 -27
- package/src/affinity.ts +13 -0
- package/src/chatter.test.ts +68 -38
- package/src/chatter.ts +269 -316
- package/src/client/PetSprite.test.tsx +50 -15
- package/src/client/PetSprite.tsx +30 -25
- package/src/client/index.test.tsx +10 -1
- package/src/client/index.ts +29 -10
- package/src/client/locales.ts +18 -0
- package/src/client/renderers/frames2d.test.ts +101 -0
- package/src/client/renderers/frames2d.ts +138 -18
- package/src/event-projection.ts +57 -16
- package/src/ledger.ts +6 -4
- package/src/pair-access.ts +49 -0
- package/src/remarks.test.ts +10 -0
- package/src/remarks.ts +9 -2
- package/src/routes.ts +9 -4
- package/src/service.ts +36 -30
- package/src/voice-pack.test.ts +87 -25
- package/src/voice-pack.ts +87 -49
package/src/voice-pack.ts
CHANGED
|
@@ -31,9 +31,13 @@
|
|
|
31
31
|
import {
|
|
32
32
|
STATUS_SCENES,
|
|
33
33
|
TOOL_CATEGORIES,
|
|
34
|
+
WHISPER_CATEGORIES,
|
|
35
|
+
WHISPER_RESULTS,
|
|
34
36
|
type VoicePackOverrides,
|
|
35
|
-
type
|
|
37
|
+
type WhisperCategory,
|
|
38
|
+
type WhisperResult,
|
|
36
39
|
} from './chatter.ts'
|
|
40
|
+
import { normalizePetRemarks, type PetRemarks } from './remarks.ts'
|
|
37
41
|
|
|
38
42
|
/** Schema version this module normalizes (optional field; missing = 1). */
|
|
39
43
|
export const VOICE_PACK_V1 = 1 as const
|
|
@@ -66,14 +70,15 @@ export interface VoicePack {
|
|
|
66
70
|
overrides: VoicePackOverrides
|
|
67
71
|
/** Hover-panel chrome, when the pack declares any. */
|
|
68
72
|
panel?: PetPanelView
|
|
73
|
+
/** Pat/feed interaction remark pools (issue #1226). */
|
|
74
|
+
remarks?: PetRemarks
|
|
75
|
+
/** Affinity rank name overrides (issue #1226). */
|
|
76
|
+
ranks?: Record<string, string>
|
|
69
77
|
}
|
|
70
78
|
|
|
71
79
|
/** Hard caps shared by every pool slot (mirrors the remarks discipline). */
|
|
72
80
|
export const VOICE_POOL_LINES_MAX = 64
|
|
73
81
|
export const VOICE_LINE_MAX = 160
|
|
74
|
-
export const VOICE_KEYWORDS_PER_RULE_MAX = 16
|
|
75
|
-
export const VOICE_KEYWORD_MAX = 40
|
|
76
|
-
export const VOICE_RULES_MAX = 32
|
|
77
82
|
export const VOICE_LABEL_MAX = 40
|
|
78
83
|
export const VOICE_STAT_MAX = 80
|
|
79
84
|
|
|
@@ -87,7 +92,7 @@ const PLACEHOLDER_WHITELIST: Partial<Record<PoolKind, readonly string[]>> = {
|
|
|
87
92
|
stat: ['{rank}', '{n}', '{points}'],
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
type PoolKind = 'status' | 'tools' | 'toolRemaining' | '
|
|
95
|
+
type PoolKind = 'status' | 'tools' | 'toolRemaining' | 'whisperCategory' | 'whisperResult' | 'label' | 'stat'
|
|
91
96
|
|
|
92
97
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
93
98
|
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
@@ -151,51 +156,48 @@ export function normalizePool(
|
|
|
151
156
|
return pool
|
|
152
157
|
}
|
|
153
158
|
|
|
154
|
-
/** Normalize
|
|
155
|
-
export function
|
|
159
|
+
/** Normalize whisper category pools; a key replaces that category's built-in pool (an explicit empty pool mutes it). */
|
|
160
|
+
export function normalizeWhisperCategories(
|
|
156
161
|
raw: unknown,
|
|
157
162
|
onWarning: (message: string) => void = () => {},
|
|
158
|
-
):
|
|
163
|
+
): Partial<Record<WhisperCategory, readonly string[]>> | undefined {
|
|
159
164
|
if (raw === undefined) return undefined
|
|
160
|
-
if (!
|
|
161
|
-
onWarning('whispers.
|
|
165
|
+
if (!isRecord(raw)) {
|
|
166
|
+
onWarning('whispers.categories must be an object')
|
|
162
167
|
return undefined
|
|
163
168
|
}
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for (const item of raw.slice(0, VOICE_RULES_MAX)) {
|
|
169
|
-
if (!isRecord(item)) {
|
|
170
|
-
onWarning('whisper rule must be an object')
|
|
169
|
+
const pools: Partial<Record<WhisperCategory, readonly string[]>> = {}
|
|
170
|
+
for (const key of Object.keys(raw)) {
|
|
171
|
+
if (!(WHISPER_CATEGORIES as readonly string[]).includes(key)) {
|
|
172
|
+
onWarning('unknown whisper category ' + key + ' ignored')
|
|
171
173
|
continue
|
|
172
174
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
onWarning('whisper rule dropped (needs keywords and a non-empty pool)')
|
|
175
|
+
const pool = normalizePool(raw[key], 'whisperCategory', onWarning)
|
|
176
|
+
if (pool !== undefined) pools[key as WhisperCategory] = pool
|
|
177
|
+
}
|
|
178
|
+
return Object.keys(pools).length > 0 ? pools : undefined
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Normalize whisper outcome pools; a key replaces that outcome's built-in pool (an explicit empty pool mutes it). */
|
|
182
|
+
export function normalizeWhisperResults(
|
|
183
|
+
raw: unknown,
|
|
184
|
+
onWarning: (message: string) => void = () => {},
|
|
185
|
+
): Partial<Record<WhisperResult, readonly string[]>> | undefined {
|
|
186
|
+
if (raw === undefined) return undefined
|
|
187
|
+
if (!isRecord(raw)) {
|
|
188
|
+
onWarning('whispers.results must be an object')
|
|
189
|
+
return undefined
|
|
190
|
+
}
|
|
191
|
+
const pools: Partial<Record<WhisperResult, readonly string[]>> = {}
|
|
192
|
+
for (const key of Object.keys(raw)) {
|
|
193
|
+
if (!(WHISPER_RESULTS as readonly string[]).includes(key)) {
|
|
194
|
+
onWarning('unknown whisper result ' + key + ' ignored')
|
|
194
195
|
continue
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
const pool = normalizePool(raw[key], 'whisperResult', onWarning)
|
|
198
|
+
if (pool !== undefined) pools[key as WhisperResult] = pool
|
|
197
199
|
}
|
|
198
|
-
return
|
|
200
|
+
return Object.keys(pools).length > 0 ? pools : undefined
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
/** Normalize the panel block (labels / stats / actions; warn-and-drop). */
|
|
@@ -268,10 +270,10 @@ export function normalizePanel(
|
|
|
268
270
|
}
|
|
269
271
|
|
|
270
272
|
/** Voice-pack top-level fields ('$schema' mirrors the schema twin; drift-locked in tests). */
|
|
271
|
-
export const VOICE_PACK_KEYS = new Set(['$schema', 'voicePackVersion', 'status', 'tools', 'toolRemaining', 'whispers', 'panel'])
|
|
273
|
+
export const VOICE_PACK_KEYS = new Set(['$schema', 'voicePackVersion', 'status', 'tools', 'toolRemaining', 'whispers', 'panel', 'remarks', 'ranks'])
|
|
272
274
|
|
|
273
275
|
/** Allowed whisper-section fields (drift-locked in tests). */
|
|
274
|
-
export const WHISPER_KEYS = new Set(['
|
|
276
|
+
export const WHISPER_KEYS = new Set(['categories', 'results'])
|
|
275
277
|
|
|
276
278
|
/**
|
|
277
279
|
* Normalize one raw voice.json document into a VoicePack, or undefined when
|
|
@@ -340,29 +342,55 @@ export function normalizeVoicePack(
|
|
|
340
342
|
onWarning('whispers must be an object')
|
|
341
343
|
} else {
|
|
342
344
|
for (const key of Object.keys(whispersRaw)) {
|
|
343
|
-
if (
|
|
345
|
+
if (key === 'generic' || key === 'rules') {
|
|
346
|
+
// pet M6: keyword and ambient whisper channels are gone; legacy
|
|
347
|
+
// fields are ignored with a warning, never mapped to the new shape.
|
|
348
|
+
onWarning('whispers.' + key + ' is no longer supported and was ignored')
|
|
349
|
+
} else if (!WHISPER_KEYS.has(key)) {
|
|
350
|
+
onWarning('unknown whispers field ' + key + ' ignored')
|
|
351
|
+
}
|
|
344
352
|
}
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
if (
|
|
353
|
+
const categories = normalizeWhisperCategories(whispersRaw.categories, onWarning)
|
|
354
|
+
const results = normalizeWhisperResults(whispersRaw.results, onWarning)
|
|
355
|
+
if (categories !== undefined || results !== undefined) {
|
|
348
356
|
overrides.whispers = {
|
|
349
|
-
...(
|
|
350
|
-
...(
|
|
357
|
+
...(categories === undefined ? {} : { categories }),
|
|
358
|
+
...(results === undefined ? {} : { results }),
|
|
351
359
|
}
|
|
352
360
|
}
|
|
353
361
|
}
|
|
354
362
|
}
|
|
355
363
|
const panel = raw.panel === undefined ? undefined : normalizePanel(raw.panel, onWarning)
|
|
364
|
+
const remarks = raw.remarks === undefined ? undefined : normalizePetRemarks(raw.remarks, onWarning)
|
|
365
|
+
const ranksRaw = raw.ranks
|
|
366
|
+
let ranks: Record<string, string> | undefined
|
|
367
|
+
if (ranksRaw !== undefined) {
|
|
368
|
+
if (!isRecord(ranksRaw)) {
|
|
369
|
+
onWarning('ranks must be an object')
|
|
370
|
+
} else {
|
|
371
|
+
const out: Record<string, string> = {}
|
|
372
|
+
for (const [key, value] of Object.entries(ranksRaw)) {
|
|
373
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
374
|
+
out[key] = value.trim().slice(0, VOICE_STAT_MAX)
|
|
375
|
+
} else {
|
|
376
|
+
onWarning('invalid rank name for ' + key)
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (Object.keys(out).length > 0) ranks = out
|
|
380
|
+
}
|
|
381
|
+
}
|
|
356
382
|
if (
|
|
357
383
|
overrides.status === undefined && overrides.tools === undefined
|
|
358
384
|
&& overrides.toolRemaining === undefined && overrides.whispers === undefined
|
|
359
|
-
&& panel === undefined
|
|
385
|
+
&& panel === undefined && remarks === undefined && ranks === undefined
|
|
360
386
|
) {
|
|
361
387
|
return undefined
|
|
362
388
|
}
|
|
363
389
|
return {
|
|
364
390
|
overrides,
|
|
365
391
|
...(panel === undefined ? {} : { panel }),
|
|
392
|
+
...(remarks === undefined ? {} : { remarks }),
|
|
393
|
+
...(ranks === undefined ? {} : { ranks }),
|
|
366
394
|
}
|
|
367
395
|
}
|
|
368
396
|
|
|
@@ -376,6 +404,8 @@ export function mergeVoicePacks(...layers: (VoicePack | undefined)[]): VoicePack
|
|
|
376
404
|
const overrides: VoicePackOverrides = {}
|
|
377
405
|
const labels: NonNullable<PetPanelView['labels']> = {}
|
|
378
406
|
const stats: NonNullable<PetPanelView['stats']> = {}
|
|
407
|
+
let remarks: PetRemarks | undefined
|
|
408
|
+
let ranks: Record<string, string> | undefined
|
|
379
409
|
let actions: PanelAction[] | undefined
|
|
380
410
|
let panelSeen = false
|
|
381
411
|
let any = false
|
|
@@ -398,6 +428,12 @@ export function mergeVoicePacks(...layers: (VoicePack | undefined)[]): VoicePack
|
|
|
398
428
|
if (layer.panel.stats !== undefined) Object.assign(stats, layer.panel.stats)
|
|
399
429
|
if (layer.panel.actions !== undefined) actions = layer.panel.actions
|
|
400
430
|
}
|
|
431
|
+
if (layer.remarks !== undefined) {
|
|
432
|
+
remarks = { ...(remarks ?? {}), ...layer.remarks }
|
|
433
|
+
}
|
|
434
|
+
if (layer.ranks !== undefined) {
|
|
435
|
+
ranks = { ...(ranks ?? {}), ...layer.ranks }
|
|
436
|
+
}
|
|
401
437
|
}
|
|
402
438
|
if (!any) return undefined
|
|
403
439
|
const panel: PetPanelView = {
|
|
@@ -409,5 +445,7 @@ export function mergeVoicePacks(...layers: (VoicePack | undefined)[]): VoicePack
|
|
|
409
445
|
return {
|
|
410
446
|
overrides,
|
|
411
447
|
...(panelSeen && !panelEmpty ? { panel } : {}),
|
|
448
|
+
...(remarks !== undefined ? { remarks } : {}),
|
|
449
|
+
...(ranks !== undefined ? { ranks } : {}),
|
|
412
450
|
}
|
|
413
451
|
}
|