Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1500f44bed |
No files matched your search
@@ -13,16 +13,19 @@ After: Nuking the database... 12s
|
||||
|
||||
## Compatibility
|
||||
|
||||
The live duration DSH appends to the label changes every second, and DSH moved the label between releases. Both layouts are handled:
|
||||
The live duration DSH appends to the label changes every second, and DSH has moved the label twice. All three layouts are handled, newest hook first:
|
||||
|
||||
| DSH version | Where the visible label lives | Path |
|
||||
| ----------------- | ------------------------------------------------------ | --------- |
|
||||
| `>= 0.1.7-alpha.2` | `button[data-turn-process]` label span | primary |
|
||||
| `<= 0.1.5-rc.2` | the visible `[role="status"]` text node | legacy |
|
||||
| DSH version | Where the visible label lives | Path |
|
||||
| ------------------ | ---------------------------------------------- | --------- |
|
||||
| `>= 0.2.0-rc.1` | `div[data-chat-running]` → `TextShimmer` label | primary |
|
||||
| `>= 0.1.7-alpha.2` | `button[data-turn-process]` label span | fallback |
|
||||
| `<= 0.1.5-rc.2` | the visible `[role="status"]` text node | legacy |
|
||||
|
||||
Labels are matched by prefix in both shipped locales — `Deep diving…` / `Deep diving for 12s` and `深度求索中…` / `深度求索中,用时12s` — so future rewording of the duration text does not break it. Completed labels (`Worked`, `Took 12s`, `Stopped`, `Failed`) are never touched: the plugin releases the label the moment the turn ends.
|
||||
Labels are matched by prefix in both shipped locales — `Deep diving…` / `Deep diving for 12s…` and `深度求索中…` / `深度求索中,用时 12s…` — so future rewording of the duration text does not break it. Completed labels (`Worked`, `Took 12s`, `Stopped`, `Failed`) are never touched: the plugin releases the label the moment the turn ends.
|
||||
|
||||
On `>= 0.1.7` the only remaining `[role="status"]` is a **visually hidden screen-reader announcement**. It is deliberately left intact, so assistive tech still hears the real status while sighted users get the joke.
|
||||
On `>= 0.1.7` the only remaining `[role="status"]` is a **visually hidden screen-reader announcement**. It is deliberately left intact, so assistive tech still hears the real status while sighted users get the joke. On `>= 0.2.0` that announcement sits *inside* the running indicator, so the plugin skips nested live regions rather than assuming the host element is the label.
|
||||
|
||||
On `>= 0.2.0` the label is rendered through `TextShimmer`, which duplicates the text into an `aria-hidden` decorative overlay. Both copies are rewritten together — otherwise the overlay paints the stock wording back on top of the rotating phrase. The collapsible process row now carries only completed text, so a finished row from an earlier turn is left alone while the current turn runs.
|
||||
|
||||
## Install
|
||||
|
||||
@@ -230,7 +233,7 @@ A DSH plugin bundle is three small pieces:
|
||||
| File | Role |
|
||||
| ------------------ | ----------------------------------------------------------------------- |
|
||||
| `index.js` | Host-side half. Deliberately a no-op — the work happens in the browser. |
|
||||
| `client.js` | Browser-half bundle: finds the status label and rewrites its text node. |
|
||||
| `client.js` | Browser-half bundle: finds the status label and rewrites its text node(s). |
|
||||
| `cordis.patch.yml` | Inserts the `based-dsh-diving` row into the `web` profile's plugin tree. |
|
||||
|
||||
`client.js` locates the label, claims its text node, and then:
|
||||
@@ -254,14 +257,21 @@ No build step, no runtime dependencies.
|
||||
node test/client.test.mjs
|
||||
```
|
||||
|
||||
The suite runs `client.js` against a minimal DOM stub and covers twelve checks:
|
||||
new-layout claim, screen-reader announcement preserved, live-duration rewrite
|
||||
re-asserted, release on turn end, staying released, legacy-layout claim, clock
|
||||
preserved, timer rotation, localized labels, clean disposal, and phrase-list
|
||||
integrity.
|
||||
The suite runs `client.js` against a minimal DOM stub and covers 22 checks across all three layouts: `0.2.0` claim with both shimmer layers kept in sync, screen-reader announcement preserved, live-duration rewrite re-asserted, precedence over a finished process row, release on turn end, staying released, `0.1.7` claim, legacy-layout claim, clock preserved, timer rotation, localized labels, clean disposal, and phrase-list integrity.
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.1.3
|
||||
- Support DSH `>= 0.2.0-rc.1`, which moved the running label out of
|
||||
`button[data-turn-process]` and into a new `div[data-chat-running]` indicator.
|
||||
- Backward compatible: the `0.1.7` and `<= 0.1.5` layouts still work, tried in
|
||||
that order after the new hook.
|
||||
- Keep `TextShimmer`'s visible and `aria-hidden` label layers in sync, so the
|
||||
decorative overlay cannot paint the stock wording back on top.
|
||||
- Skip nested live regions while scanning, keeping the screen-reader
|
||||
announcement intact even though it now sits inside the label host.
|
||||
- Leave finished `[data-turn-process]` rows alone while a turn is running.
|
||||
|
||||
### 0.1.2
|
||||
- Support the DSH `>= 0.1.7-alpha.2` label, which moved out of `[role="status"]`
|
||||
and into `button[data-turn-process]`.
|
||||
|
||||
@@ -166,17 +166,22 @@ window.__ModuleLoader__.load({
|
||||
var ROTATE_MS = 2200;
|
||||
var SCAN_DEBOUNCE_MS = 120;
|
||||
|
||||
// The stock running-turn label, across locales and both DOM layouts:
|
||||
// The stock running-turn label, across locales and every DOM layout shipped
|
||||
// so far:
|
||||
//
|
||||
// DSH <= 0.1.5 "Deep diving..." / "深度求索中..."
|
||||
// DSH >= 0.1.7 "Deep diving for 12s" / "深度求索中,用时12s"
|
||||
// DSH <= 0.1.5 "Deep diving..." / "深度求索中..."
|
||||
// DSH 0.1.7 "Deep diving for 12s" / "深度求索中,用时12s"
|
||||
// DSH >= 0.2.0 "Deep diving for 12s..." / "深度求索中,用时 12s..."
|
||||
//
|
||||
// Completed labels ("Worked", "Took 12s", "Stopped", "Failed",
|
||||
// "已完成工作", "用时 12s", "处理失败") never begin with these prefixes, so a
|
||||
// prefix match is enough and survives future rewording.
|
||||
var RUNNING_LABEL = /^\s*(?:deep\s*diving|深度求索中)/i;
|
||||
|
||||
// host element -> { node, text } for every label we currently own.
|
||||
// host element -> { nodes, text } for every label we currently own. A host
|
||||
// can carry more than one copy of the label: TextShimmer renders the text in
|
||||
// a visible layer plus an aria-hidden overlay, and both must move together or
|
||||
// the overlay paints the stock wording back on top of the rotating phrase.
|
||||
var owned = new WeakMap();
|
||||
|
||||
var lastIndex = -1;
|
||||
@@ -195,42 +200,54 @@ window.__ModuleLoader__.load({
|
||||
}
|
||||
|
||||
/**
|
||||
* Elements that may host the visible running-turn label.
|
||||
* Elements that may host the visible running-turn label, newest layout first.
|
||||
*
|
||||
* DSH >= 0.1.7 renders the label inside a <button data-turn-process>; its
|
||||
* only remaining [role="status"] is a visually hidden screen-reader
|
||||
* announcement, which we deliberately leave alone. Older releases rendered
|
||||
* the label as the visible [role="status"] text itself.
|
||||
* [data-chat-running] DSH >= 0.2.0: the blue "running" indicator that
|
||||
* carries the live "Deep diving for 12s..." label.
|
||||
* [data-turn-process] DSH 0.1.7: the collapsible process row, which
|
||||
* then carried the running label.
|
||||
* [role="status"] DSH <= 0.1.5: the label *was* the status text.
|
||||
*
|
||||
* Later releases leave [role="status"] to a visually hidden screen-reader
|
||||
* announcement, so it is only ever used as a last-resort fallback, and never
|
||||
* when a newer hook is present.
|
||||
*/
|
||||
function labelHosts() {
|
||||
var hosts = [];
|
||||
var buttons = document.querySelectorAll("[data-turn-process]");
|
||||
if (buttons.length > 0) {
|
||||
for (var i = 0; i < buttons.length; i += 1) hosts.push(buttons[i]);
|
||||
return hosts;
|
||||
var layouts = ["[data-chat-running]", "[data-turn-process]"];
|
||||
for (var i = 0; i < layouts.length; i += 1) {
|
||||
var found = document.querySelectorAll(layouts[i]);
|
||||
if (found.length > 0) return found;
|
||||
}
|
||||
var statuses = document.querySelectorAll('[role="status"]');
|
||||
for (var j = 0; j < statuses.length; j += 1) hosts.push(statuses[j]);
|
||||
return hosts;
|
||||
return document.querySelectorAll('[role="status"]');
|
||||
}
|
||||
|
||||
/** First text descendant still carrying the stock running label. */
|
||||
function findLabelNode(host) {
|
||||
var record = owned.get(host);
|
||||
if (record && isLive(record.node) && host.contains(record.node)) return record.node;
|
||||
/** Live-region element: DSH's screen-reader announcement, never rewritten. */
|
||||
function isAnnouncement(element) {
|
||||
return typeof element.getAttribute === "function" &&
|
||||
element.getAttribute("role") === "status";
|
||||
}
|
||||
|
||||
/**
|
||||
* Every text descendant still carrying the stock running label.
|
||||
*
|
||||
* Returns a list rather than one node because TextShimmer duplicates the
|
||||
* label into a decorative overlay; skipping nested live regions keeps the
|
||||
* visually hidden announcement out of it.
|
||||
*/
|
||||
function findLabelNodes(host) {
|
||||
var nodes = [];
|
||||
var stack = [host];
|
||||
while (stack.length > 0) {
|
||||
var node = stack.pop();
|
||||
for (var child = node.firstChild; child; child = child.nextSibling) {
|
||||
if (child.nodeType === 3) {
|
||||
if (RUNNING_LABEL.test(child.nodeValue || "")) return child;
|
||||
if (RUNNING_LABEL.test(child.nodeValue || "")) nodes.push(child);
|
||||
} else if (child.nodeType === 1) {
|
||||
stack.push(child);
|
||||
if (!isAnnouncement(child)) stack.push(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,30 +259,35 @@ window.__ModuleLoader__.load({
|
||||
|
||||
var record = owned.get(host);
|
||||
|
||||
if (!record) {
|
||||
var fresh = findLabelNode(host);
|
||||
if (!fresh) return;
|
||||
var text = nextPhrase();
|
||||
fresh.nodeValue = text;
|
||||
owned.set(host, { node: fresh, text: text });
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLive(record.node) || !host.contains(record.node)) {
|
||||
if (record) {
|
||||
var alive = [];
|
||||
for (var i = 0; i < record.nodes.length; i += 1) {
|
||||
if (isLive(record.nodes[i]) && host.contains(record.nodes[i])) alive.push(record.nodes[i]);
|
||||
}
|
||||
if (alive.length > 0) {
|
||||
record.nodes = alive;
|
||||
for (var j = 0; j < alive.length; j += 1) {
|
||||
var current = alive[j].nodeValue || "";
|
||||
if (current === record.text) continue;
|
||||
if (RUNNING_LABEL.test(current)) {
|
||||
alive[j].nodeValue = record.text;
|
||||
continue;
|
||||
}
|
||||
// The label changed meaning (the turn finished): hands off for good.
|
||||
// Staying claimed would fight the final "Worked for 12s" copy.
|
||||
owned.delete(host);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
owned.delete(host);
|
||||
claim(host);
|
||||
return;
|
||||
}
|
||||
|
||||
var current = record.node.nodeValue || "";
|
||||
if (current === record.text) return;
|
||||
if (RUNNING_LABEL.test(current)) {
|
||||
record.node.nodeValue = record.text;
|
||||
return;
|
||||
}
|
||||
// The turn finished (or the label changed meaning): hands off for good.
|
||||
// Leaving this claimed would fight the final "Worked for 12s" text.
|
||||
owned.delete(host);
|
||||
var fresh = findLabelNodes(host);
|
||||
if (fresh.length === 0) return;
|
||||
var text = nextPhrase();
|
||||
for (var k = 0; k < fresh.length; k += 1) fresh[k].nodeValue = text;
|
||||
owned.set(host, { nodes: fresh, text: text });
|
||||
}
|
||||
|
||||
function scan() {
|
||||
@@ -285,27 +307,29 @@ window.__ModuleLoader__.load({
|
||||
claim(host);
|
||||
continue;
|
||||
}
|
||||
if (!isLive(record.node) || !host.contains(record.node)) {
|
||||
var stale = false;
|
||||
for (var j = 0; j < record.nodes.length; j += 1) {
|
||||
var node = record.nodes[j];
|
||||
if (!isLive(node) || !host.contains(node)) { stale = true; break; }
|
||||
var current = node.nodeValue || "";
|
||||
if (current !== record.text && !RUNNING_LABEL.test(current)) { stale = true; break; }
|
||||
}
|
||||
if (stale) {
|
||||
owned.delete(host);
|
||||
claim(host);
|
||||
continue;
|
||||
}
|
||||
var current = record.node.nodeValue || "";
|
||||
if (current !== record.text && !RUNNING_LABEL.test(current)) {
|
||||
owned.delete(host);
|
||||
continue;
|
||||
}
|
||||
var text = nextPhrase();
|
||||
record.text = text;
|
||||
record.node.nodeValue = text;
|
||||
for (var k = 0; k < record.nodes.length; k += 1) record.nodes[k].nodeValue = text;
|
||||
}
|
||||
}
|
||||
|
||||
/** Nearest turn-process button above a mutated text node, if any. */
|
||||
/** Nearest running-status host above a mutated text node, if any. */
|
||||
function hostOf(node) {
|
||||
var element = node && node.parentElement;
|
||||
if (!element || typeof element.closest !== "function") return null;
|
||||
return element.closest("[data-turn-process]");
|
||||
return element.closest("[data-chat-running], [data-turn-process]");
|
||||
}
|
||||
|
||||
function apply(ctx) {
|
||||
@@ -337,9 +361,10 @@ window.__ModuleLoader__.load({
|
||||
structural = true;
|
||||
continue;
|
||||
}
|
||||
// Fast path: DSH >= 0.1.7 rewrites the live duration every
|
||||
// second. Observer callbacks run before paint, so restoring here
|
||||
// keeps the stock label from flashing through.
|
||||
// Fast path: DSH rewrites the live duration every second (in
|
||||
// 0.2.0 that is two shimmer layers at once). Observer callbacks
|
||||
// run before paint, so restoring here keeps the stock label from
|
||||
// flashing through.
|
||||
var host = hostOf(record.target);
|
||||
if (host) claim(host);
|
||||
else structural = true;
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@based-dsh-diving/dsh-plugin",
|
||||
"description": "Replace DeepSeek Harness's running-turn status label with rotating, chronically online loading text.",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"exports": {
|
||||
|
||||
+120
-27
@@ -1,6 +1,9 @@
|
||||
// Headless harness for based-dsh-diving/client.js.
|
||||
//
|
||||
// Stubs just enough DOM to exercise both supported layouts:
|
||||
// Stubs just enough DOM to exercise all three supported layouts:
|
||||
// - DSH >= 0.2.0: <div data-chat-running> holding a hidden [role="status"]
|
||||
// announcement plus a TextShimmer whose label is rendered
|
||||
// twice (visible layer + aria-hidden decorative overlay)
|
||||
// - DSH >= 0.1.7: <button data-turn-process> > <span>label</span>
|
||||
// plus a visually hidden [role="status"] announcement
|
||||
// - DSH <= 0.1.5: the visible label is the [role="status"] text itself
|
||||
@@ -51,6 +54,7 @@ class El {
|
||||
get isConnected() { return this.rooted || isConnected(this); }
|
||||
get firstChild() { return this.childNodes[0] ?? null; }
|
||||
get firstElementChild() { return this.childNodes.find((n) => n.nodeType === 1) ?? null; }
|
||||
getAttribute(name) { return this.attrs[name] === undefined ? null : String(this.attrs[name]); }
|
||||
append(...nodes) {
|
||||
for (const node of nodes) { node.parent = this; this.childNodes.push(node); }
|
||||
this.childNodes.forEach((n, i) => { n.nextSibling = this.childNodes[i + 1] ?? null; });
|
||||
@@ -65,9 +69,13 @@ class El {
|
||||
|
||||
function matches(el, selector) {
|
||||
if (el.nodeType !== 1) return false;
|
||||
if (selector === "[data-turn-process]") return el.attrs["data-turn-process"] !== undefined;
|
||||
if (selector === '[role="status"]') return el.attrs.role === "status";
|
||||
return false;
|
||||
return selector.split(",").some((raw) => {
|
||||
const part = raw.trim();
|
||||
if (part === "[data-turn-process]") return el.attrs["data-turn-process"] !== undefined;
|
||||
if (part === "[data-chat-running]") return el.attrs["data-chat-running"] !== undefined;
|
||||
if (part === '[role="status"]') return el.attrs.role === "status";
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function allElements(root) {
|
||||
@@ -105,14 +113,107 @@ const start = () => {
|
||||
mod.apply({ effect: (fn) => { dispose = fn(); } });
|
||||
return { dispose };
|
||||
};
|
||||
const isJoke = (value) => /\.\.\.$/.test(value);
|
||||
// "Deep diving" is itself one of the phrases, so compare against the stock text
|
||||
// this layout shipped rather than pattern-matching the joke wording.
|
||||
const isJoke = (value, stock) => /\.\.\.$/.test(value) && value !== stock;
|
||||
const fire = (...nodes) => observerCallback?.(nodes.map((target) => ({ type: "characterData", target })));
|
||||
|
||||
// ------------------------------------------------------- new layout (>= 0.1.7)
|
||||
/** Build DSH >= 0.2.0's RunningStatus subtree and return its label text nodes. */
|
||||
function buildRunningStatus(root, labelText) {
|
||||
const running = new El("div", { "data-chat-running": "true", class: "running" });
|
||||
const hidden = new El("span", { role: "status", "aria-live": "polite", "aria-atomic": "true" });
|
||||
const announcement = new TextNode("Deep diving...");
|
||||
hidden.append(announcement);
|
||||
|
||||
const divider = new El("span", { "aria-hidden": "true" });
|
||||
|
||||
const content = new El("span", { class: "runningContent" });
|
||||
const shimmer = new El("span", { "data-shimmer": "true" });
|
||||
const visibleLayer = new El("span", { class: "content" });
|
||||
const visible = new TextNode(labelText);
|
||||
visibleLayer.append(visible);
|
||||
|
||||
const decoration = new El("span", { "aria-hidden": "true" });
|
||||
const sweep = new El("span", { class: "sweep" });
|
||||
const highlightLayer = new El("span", { class: "content highlight" });
|
||||
const highlight = new TextNode(labelText);
|
||||
highlightLayer.append(highlight);
|
||||
sweep.append(highlightLayer);
|
||||
decoration.append(sweep);
|
||||
|
||||
shimmer.append(visibleLayer, decoration);
|
||||
content.append(shimmer);
|
||||
running.append(hidden, divider, content);
|
||||
root.append(running);
|
||||
return { running, visible, highlight, announcement };
|
||||
}
|
||||
|
||||
// -------------------------------------------- current layout (DSH >= 0.2.0)
|
||||
|
||||
{
|
||||
const root = useDom();
|
||||
const { visible, highlight, announcement } = buildRunningStatus(root, "Deep diving for 1s...");
|
||||
|
||||
const session = start();
|
||||
const owned = visible.nodeValue;
|
||||
|
||||
ok("0.2.0: visible shimmer layer claimed", isJoke(owned, "Deep diving for 1s..."), JSON.stringify(owned));
|
||||
ok("0.2.0: decorative overlay stays in sync", highlight.nodeValue === owned, JSON.stringify(highlight.nodeValue));
|
||||
ok("0.2.0: screen-reader announcement untouched", announcement.nodeValue === "Deep diving...", JSON.stringify(announcement.nodeValue));
|
||||
|
||||
// React rewrites the live duration every second, in both layers at once.
|
||||
visible.nodeValue = "Deep diving for 2s...";
|
||||
highlight.nodeValue = "Deep diving for 2s...";
|
||||
fire(visible, highlight);
|
||||
ok("0.2.0: live-duration rewrite re-asserted (visible)", visible.nodeValue === owned, JSON.stringify(visible.nodeValue));
|
||||
ok("0.2.0: live-duration rewrite re-asserted (overlay)", highlight.nodeValue === owned, JSON.stringify(highlight.nodeValue));
|
||||
|
||||
// Turn ends: the plugin must release, not fight the final text.
|
||||
visible.nodeValue = "Worked for 12s";
|
||||
highlight.nodeValue = "Worked for 12s";
|
||||
fire(visible, highlight);
|
||||
ok("0.2.0: released on turn end", visible.nodeValue === "Worked for 12s", JSON.stringify(visible.nodeValue));
|
||||
|
||||
const before = visible.nodeValue;
|
||||
await wait(2400);
|
||||
ok("0.2.0: stays released after a rotation tick", visible.nodeValue === before, JSON.stringify(visible.nodeValue));
|
||||
|
||||
session.dispose();
|
||||
}
|
||||
|
||||
// ------------------------------- precedence: a stale completed process row
|
||||
// On 0.2.0 the collapsible process row still exists for finished turns, so the
|
||||
// running indicator must win and the finished row must be left alone.
|
||||
|
||||
{
|
||||
const root = useDom();
|
||||
const done = new El("button", { "data-turn-process": "t0" });
|
||||
const doneLabel = new TextNode("Worked for 40s");
|
||||
const doneSpan = new El("span", { class: "label" });
|
||||
doneSpan.append(doneLabel);
|
||||
done.append(doneSpan);
|
||||
root.append(done);
|
||||
|
||||
const { visible } = buildRunningStatus(root, "Deep diving for 1s...");
|
||||
const session = start();
|
||||
|
||||
ok("0.2.0: running indicator wins over finished row", isJoke(visible.nodeValue, "Deep diving for 1s..."), JSON.stringify(visible.nodeValue));
|
||||
ok("0.2.0: finished process row untouched", doneLabel.nodeValue === "Worked for 40s", JSON.stringify(doneLabel.nodeValue));
|
||||
|
||||
// A finished row that React later rewrites must not be claimed either.
|
||||
doneLabel.nodeValue = "Took 41s";
|
||||
fire(doneLabel);
|
||||
ok("0.2.0: finished row never claimed", doneLabel.nodeValue === "Took 41s", JSON.stringify(doneLabel.nodeValue));
|
||||
|
||||
session.dispose();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------- 0.1.7 row
|
||||
|
||||
{
|
||||
const root = useDom();
|
||||
const button = new El("button", { "data-turn-process": "t1" });
|
||||
const labelSpan = new El("span");
|
||||
const labelSpan = new El("span", { class: "label" });
|
||||
const label = new TextNode("Deep diving for 1s");
|
||||
labelSpan.append(label);
|
||||
button.append(labelSpan);
|
||||
@@ -124,23 +225,20 @@ const isJoke = (value) => /\.\.\.$/.test(value);
|
||||
const session = start();
|
||||
const owned = label.nodeValue;
|
||||
|
||||
ok("new layout: label claimed", isJoke(owned), JSON.stringify(owned));
|
||||
ok("new layout: screen-reader announcement untouched", announcement.nodeValue === "Deep diving...", JSON.stringify(announcement.nodeValue));
|
||||
ok("0.1.7: label claimed", isJoke(owned, "Deep diving for 1s"), JSON.stringify(owned));
|
||||
ok("0.1.7: screen-reader announcement untouched", announcement.nodeValue === "Deep diving...", JSON.stringify(announcement.nodeValue));
|
||||
|
||||
// React rewrites the live duration every second; the observer must restore
|
||||
// synchronously, before paint.
|
||||
label.nodeValue = "Deep diving for 2s";
|
||||
observerCallback?.([{ type: "characterData", target: label }]);
|
||||
ok("new layout: live-duration rewrite re-asserted", label.nodeValue === owned, JSON.stringify(label.nodeValue));
|
||||
fire(label);
|
||||
ok("0.1.7: live-duration rewrite re-asserted", label.nodeValue === owned, JSON.stringify(label.nodeValue));
|
||||
|
||||
// Turn ends: the plugin must release the label, not fight the final text.
|
||||
label.nodeValue = "Worked for 12s";
|
||||
observerCallback?.([{ type: "characterData", target: label }]);
|
||||
ok("new layout: released on turn end", label.nodeValue === "Worked for 12s", JSON.stringify(label.nodeValue));
|
||||
fire(label);
|
||||
ok("0.1.7: released on turn end", label.nodeValue === "Worked for 12s", JSON.stringify(label.nodeValue));
|
||||
|
||||
const before = label.nodeValue;
|
||||
await wait(2400);
|
||||
ok("new layout: stays released after a rotation tick", label.nodeValue === before, JSON.stringify(label.nodeValue));
|
||||
ok("0.1.7: stays released after a rotation tick", label.nodeValue === before, JSON.stringify(label.nodeValue));
|
||||
|
||||
session.dispose();
|
||||
}
|
||||
@@ -160,7 +258,7 @@ const isJoke = (value) => /\.\.\.$/.test(value);
|
||||
const session = start();
|
||||
const owned = label.nodeValue;
|
||||
|
||||
ok("legacy layout: label claimed", isJoke(owned), JSON.stringify(owned));
|
||||
ok("legacy layout: label claimed", isJoke(owned, "Deep diving..."), JSON.stringify(owned));
|
||||
ok("legacy layout: clock untouched", clockText.nodeValue === "12s", JSON.stringify(clockText.nodeValue));
|
||||
|
||||
await wait(2400);
|
||||
@@ -173,20 +271,15 @@ const isJoke = (value) => /\.\.\.$/.test(value);
|
||||
|
||||
{
|
||||
const root = useDom();
|
||||
const button = new El("button", { "data-turn-process": "t2" });
|
||||
const span = new El("span");
|
||||
const label = new TextNode("深度求索中,用时3s");
|
||||
span.append(label);
|
||||
button.append(span);
|
||||
root.append(button);
|
||||
const { visible } = buildRunningStatus(root, "深度求索中,用时 3s...");
|
||||
|
||||
const session = start();
|
||||
ok("localized running label claimed", isJoke(label.nodeValue) && !/深度求索中/.test(label.nodeValue), JSON.stringify(label.nodeValue));
|
||||
ok("localized running label claimed", isJoke(visible.nodeValue, "深度求索中,用时 3s...") && !/深度求索中/.test(visible.nodeValue), JSON.stringify(visible.nodeValue));
|
||||
|
||||
const frozen = label.nodeValue;
|
||||
const frozen = visible.nodeValue;
|
||||
session.dispose();
|
||||
await wait(2400);
|
||||
ok("disposer stops rotation", label.nodeValue === frozen, JSON.stringify(label.nodeValue));
|
||||
ok("disposer stops rotation", visible.nodeValue === frozen, JSON.stringify(visible.nodeValue));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------- static integrity
|
||||
|
||||
Reference in new issue
Block a user