@kukonce/dsh-loomy 0.3.3 → 0.3.4
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 +17 -0
- package/client/index.js +78 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.4 — 2026-11-14
|
|
4
|
+
|
|
5
|
+
### Fixed — the pill survives the default-model boot
|
|
6
|
+
|
|
7
|
+
A fresh conversation shows the session's default model without writing a selection: the host's
|
|
8
|
+
modelSelection projection carries `next: null` (the Agent's initial options supply the route)
|
|
9
|
+
and `lastUsed: null` (no request yet). A projection-only gate read that as "no provider" and hid
|
|
10
|
+
the pill although a Loomy model was on screen — Loomy's own catalog default (Spark-X2.6) made
|
|
11
|
+
this the common case.
|
|
12
|
+
|
|
13
|
+
The gate now also reads the composer's model trigger from the DOM (`span[data-provider-id]`,
|
|
14
|
+
rendered by the model-selection package): if the visible trigger is a Loomy route, the pill
|
|
15
|
+
mounts even with a null projection. A 2-second watcher notices trigger changes and wakes the
|
|
16
|
+
gate; the projection stays authoritative while it has data (an in-flight switch beats the DOM).
|
|
17
|
+
|
|
18
|
+
Code paths otherwise unchanged.
|
|
19
|
+
|
|
3
20
|
## 0.3.3 — 2026-11-14
|
|
4
21
|
|
|
5
22
|
README 的效果图改走 npm CDN(jsdelivr),不再依赖 GitHub 仓库可达——账号暂停期间 npm 网页上图片也能打开。代码与 0.3.2 一致。
|
package/client/index.js
CHANGED
|
@@ -1003,14 +1003,91 @@ window.__ModuleLoader__.load({
|
|
|
1003
1003
|
open ? h(PillPopover, { t, reading, stale, error, updatedAt }) : null)
|
|
1004
1004
|
}
|
|
1005
1005
|
|
|
1006
|
+
/**
|
|
1007
|
+
* What the composer's model trigger is currently DISPLAYING, read from the DOM.
|
|
1008
|
+
*
|
|
1009
|
+
* The modelSelection projection's `next` is null while the Agent's initial options
|
|
1010
|
+
* supply the route and `lastUsed` is null before the first request — so on a fresh
|
|
1011
|
+
* conversation that merely SHOWS the default model, both are null and a
|
|
1012
|
+
* projection-only gate would hide the pill although a Loomy model is on screen.
|
|
1013
|
+
*
|
|
1014
|
+
* The composer's model trigger is rendered by the model-selection package as
|
|
1015
|
+
* `<span data-provider-id="..." data-model-id="...">` — that attribute is the
|
|
1016
|
+
* authoritative record of what is on screen, and reading it costs nothing and
|
|
1017
|
+
* occupies no slot (registering a component on the label slot would replace the
|
|
1018
|
+
* host's fallback renderer and blank the model name).
|
|
1019
|
+
*
|
|
1020
|
+
* A module-level watcher polls that attribute every few seconds and, whenever the
|
|
1021
|
+
* visible provider CHANGES, bumps a counter and dispatches an event so a mounted
|
|
1022
|
+
* gate re-renders on the transition — the projection subscription alone never
|
|
1023
|
+
* fires for the default-model boot case.
|
|
1024
|
+
*/
|
|
1025
|
+
const TRIGGER_WATCH = {
|
|
1026
|
+
timer: null,
|
|
1027
|
+
provider: undefined,
|
|
1028
|
+
version: 0,
|
|
1029
|
+
listeners: new Set()
|
|
1030
|
+
}
|
|
1031
|
+
const TRIGGER_EVENT = 'loomy:trigger-provider-changed'
|
|
1032
|
+
|
|
1033
|
+
function visibleTriggerProvider() {
|
|
1034
|
+
if (typeof document === 'undefined') return undefined
|
|
1035
|
+
const nodes = document.querySelectorAll('span[data-provider-id]')
|
|
1036
|
+
if (nodes.length === 0) return undefined
|
|
1037
|
+
// The composer shows exactly one model label per open surface; menus/rows exist
|
|
1038
|
+
// only while open. The last match is the live one; any loomy match wins anyway
|
|
1039
|
+
// because the gate only asks "is loomy on screen".
|
|
1040
|
+
let seen = undefined
|
|
1041
|
+
for (const node of nodes) {
|
|
1042
|
+
const id = node.getAttribute('data-provider-id')
|
|
1043
|
+
if (typeof id === 'string' && id.length > 0) seen = id
|
|
1044
|
+
}
|
|
1045
|
+
return seen
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function watchTriggerProvider() {
|
|
1049
|
+
if (TRIGGER_WATCH.timer !== null || typeof document === 'undefined') return
|
|
1050
|
+
const tick = () => {
|
|
1051
|
+
const provider = visibleTriggerProvider()
|
|
1052
|
+
if (provider !== TRIGGER_WATCH.provider) {
|
|
1053
|
+
TRIGGER_WATCH.provider = provider
|
|
1054
|
+
TRIGGER_WATCH.version += 1
|
|
1055
|
+
try { document.dispatchEvent(new CustomEvent(TRIGGER_EVENT)) } catch {}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
tick()
|
|
1059
|
+
TRIGGER_WATCH.timer = setInterval(tick, 2000)
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function unwatchTriggerProvider() {
|
|
1063
|
+
if (TRIGGER_WATCH.timer !== null) {
|
|
1064
|
+
clearInterval(TRIGGER_WATCH.timer)
|
|
1065
|
+
TRIGGER_WATCH.timer = null
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1006
1069
|
/**
|
|
1007
1070
|
* The gate: calls `useProjection` exactly once per render, unconditionally, and
|
|
1008
|
-
* mounts the pill only when the conversation points at a Loomy account.
|
|
1071
|
+
* mounts the pill only when the conversation points at a Loomy account. The
|
|
1072
|
+
* projection is preferred (it knows an in-flight switch before the DOM catches
|
|
1073
|
+
* up); the trigger watcher fills the default-model gap described above. Any other
|
|
1009
1074
|
* provider renders nothing and starts nothing — the polling hook lives inside the
|
|
1010
1075
|
* pill body, so a hidden pill also makes no requests.
|
|
1011
1076
|
*/
|
|
1012
1077
|
function LoomyComposerPill(props) {
|
|
1078
|
+
const [, bumpVersion] = React.useState(0)
|
|
1079
|
+
React.useEffect(() => {
|
|
1080
|
+
const onChange = () => bumpVersion((current) => current + 1)
|
|
1081
|
+
document.addEventListener(TRIGGER_EVENT, onChange)
|
|
1082
|
+
watchTriggerProvider()
|
|
1083
|
+
return () => document.removeEventListener(TRIGGER_EVENT, onChange)
|
|
1084
|
+
}, [])
|
|
1013
1085
|
const provider = selectedProviderOf(props.useProjection?.('modelSelection'))
|
|
1086
|
+
?? (TRIGGER_WATCH.provider !== undefined &&
|
|
1087
|
+
typeof TRIGGER_WATCH.provider === 'string' &&
|
|
1088
|
+
TRIGGER_WATCH.provider.startsWith('@loomy/dsh-loomy/')
|
|
1089
|
+
? TRIGGER_WATCH.provider
|
|
1090
|
+
: undefined)
|
|
1014
1091
|
if (typeof provider !== 'string' || !provider.startsWith('@loomy/dsh-loomy/')) return null
|
|
1015
1092
|
return h(LoomyPillBody, { t: props.t })
|
|
1016
1093
|
}
|
package/package.json
CHANGED