@iasiv5/dsh-quota-watch 0.0.16 → 0.0.18

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/lib/client.js CHANGED
@@ -166,7 +166,7 @@ var STYLE_TEXT = `
166
166
  .dqw-stale-mark { flex: none; font-size: 9px; line-height: 14px; color: var(--dsw-alias-label-warning, #b46900); cursor: help; }
167
167
  .dqw-errtext { font-size: 10px; line-height: 14px; opacity: .7; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
168
168
  .dqw-rail-trigger { display: none; flex: none; align-items: center; justify-content: center; width: 36px; height: 36px; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; transition: background-color .12s, color .12s; }
169
- .dqw-rail-trigger svg { display: block; width: 22px; height: 22px; }
169
+ .dqw-rail-trigger svg { display: block; width: 16px; height: 16px; }
170
170
  .dqw-rail-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
171
171
  .dqw-rail-trigger:active:not(:disabled), .dqw-rail-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-active, rgba(128,128,128,.18)); color: var(--dsw-alias-label-primary, inherit); }
172
172
  .dqw-rail-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: 2px; }
@@ -252,11 +252,11 @@ function quotaIcon(doc) {
252
252
  const namespace = "http://www.w3.org/2000/svg";
253
253
  const svg = doc.createElementNS(namespace, "svg");
254
254
  svg.setAttribute("viewBox", "0 0 24 24");
255
- svg.setAttribute("width", "22");
256
- svg.setAttribute("height", "22");
255
+ svg.setAttribute("width", "16");
256
+ svg.setAttribute("height", "16");
257
257
  svg.setAttribute("fill", "none");
258
258
  svg.setAttribute("stroke", "currentColor");
259
- svg.setAttribute("stroke-width", "1.7");
259
+ svg.setAttribute("stroke-width", "2");
260
260
  svg.setAttribute("stroke-linecap", "round");
261
261
  svg.setAttribute("stroke-linejoin", "round");
262
262
  svg.setAttribute("aria-hidden", "true");
@@ -264,15 +264,15 @@ function quotaIcon(doc) {
264
264
  const dial = doc.createElementNS(namespace, "circle");
265
265
  dial.setAttribute("cx", "12");
266
266
  dial.setAttribute("cy", "12");
267
- dial.setAttribute("r", "9");
267
+ dial.setAttribute("r", "10");
268
268
  const ticks = doc.createElementNS(namespace, "path");
269
- ticks.setAttribute("d", "M12 2.7v2.1M12 19.2v2.1M2.7 12h2.1M19.2 12h2.1M5.43 5.43l1.49 1.49M17.08 17.08l1.49 1.49M18.57 5.43l-1.49 1.49M6.92 17.08l-1.49 1.49");
269
+ ticks.setAttribute("d", "M12 2.4v1.6M12 20v1.6M2.4 12h1.6M20 12h1.6M5.2 5.2l1.1 1.1M18.8 18.8l-1.1-1.1M18.8 5.2l-1.1 1.1M5.2 18.8l1.1-1.1");
270
270
  const needle = doc.createElementNS(namespace, "path");
271
271
  needle.setAttribute("d", "M12 12l4.5-4");
272
272
  const hub = doc.createElementNS(namespace, "circle");
273
273
  hub.setAttribute("cx", "12");
274
274
  hub.setAttribute("cy", "12");
275
- hub.setAttribute("r", "1.2");
275
+ hub.setAttribute("r", "1.4");
276
276
  hub.setAttribute("fill", "currentColor");
277
277
  hub.setAttribute("stroke", "none");
278
278
  svg.append(dial, ticks, needle, hub);
@@ -363,7 +363,7 @@ function mountQuotaCard({
363
363
  container.dataset.dshQuotaWatchCard = "";
364
364
  container.dataset.dshPlugin = "quota-watch";
365
365
  container.dataset.dshPart = "sidebar-card";
366
- const style = text(doc, "style", "", STYLE_TEXT);
366
+ let style = text(doc, "style", "", STYLE_TEXT);
367
367
  const card = doc.createElement("section");
368
368
  card.className = "dqw-card";
369
369
  card.setAttribute("aria-label", copy.title);
@@ -383,6 +383,25 @@ function mountQuotaCard({
383
383
  body.append(lastUpdated);
384
384
  card.append(body);
385
385
  container.append(style, card, railTrigger);
386
+ const supportsAdopted = Array.isArray(doc.adoptedStyleSheets) && typeof win.CSSStyleSheet === "function";
387
+ let adoptedSheet = null;
388
+ if (supportsAdopted) {
389
+ try {
390
+ adoptedSheet = new win.CSSStyleSheet();
391
+ adoptedSheet.replaceSync(STYLE_TEXT);
392
+ doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, adoptedSheet];
393
+ } catch {
394
+ adoptedSheet = null;
395
+ }
396
+ }
397
+ let styleAlive = () => doc.contains(style) && style.sheet !== null && style.sheet.cssRules.length > 0;
398
+ let ensureStyleAlive = () => {
399
+ if (styleAlive()) return;
400
+ const fresh = text(doc, "style", "", STYLE_TEXT);
401
+ if (doc.contains(style)) style.replaceWith(fresh);
402
+ else container.prepend(fresh);
403
+ style = fresh;
404
+ };
386
405
  const pop = doc.createElement("div");
387
406
  pop.className = "dqw-pop";
388
407
  pop.id = "dqw-quota-watch-pop";
@@ -659,6 +678,7 @@ function mountQuotaCard({
659
678
  positionPop();
660
679
  };
661
680
  const renderAll = () => {
681
+ ensureStyleAlive();
662
682
  const providers = snapshot?.providers ?? [];
663
683
  const glm = providers.find((provider) => provider?.key === "glm");
664
684
  const copilot = providers.find((provider) => provider?.key === "copilot");
@@ -840,6 +860,9 @@ function mountQuotaCard({
840
860
  pop.removeEventListener("click", onPopClick);
841
861
  container.remove();
842
862
  pop.remove();
863
+ if (adoptedSheet !== null && Array.isArray(doc.adoptedStyleSheets)) {
864
+ doc.adoptedStyleSheets = doc.adoptedStyleSheets.filter((sheet) => sheet !== adoptedSheet);
865
+ }
843
866
  };
844
867
  }
845
868
  function apply(ctx) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iasiv5/dsh-quota-watch",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "description": "DSH Web sidebar monitor for GLM Coding Plan quotas and GitHub Copilot credits",
5
5
  "type": "module",
6
6
  "main": "./src/host.mjs",
package/src/client.mjs CHANGED
@@ -88,7 +88,7 @@ const STYLE_TEXT = `
88
88
  .dqw-stale-mark { flex: none; font-size: 9px; line-height: 14px; color: var(--dsw-alias-label-warning, #b46900); cursor: help; }
89
89
  .dqw-errtext { font-size: 10px; line-height: 14px; opacity: .7; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
90
90
  .dqw-rail-trigger { display: none; flex: none; align-items: center; justify-content: center; width: 36px; height: 36px; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--dsw-alias-label-primary, inherit); cursor: pointer; transition: background-color .12s, color .12s; }
91
- .dqw-rail-trigger svg { display: block; width: 22px; height: 22px; }
91
+ .dqw-rail-trigger svg { display: block; width: 16px; height: 16px; }
92
92
  .dqw-rail-trigger:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover, rgba(128,128,128,.12)); color: var(--dsw-alias-label-primary, inherit); }
93
93
  .dqw-rail-trigger:active:not(:disabled), .dqw-rail-trigger[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-active, rgba(128,128,128,.18)); color: var(--dsw-alias-label-primary, inherit); }
94
94
  .dqw-rail-trigger:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary, #5b8def); outline-offset: 2px; }
@@ -181,11 +181,11 @@ function quotaIcon(doc) {
181
181
  const namespace = 'http://www.w3.org/2000/svg'
182
182
  const svg = doc.createElementNS(namespace, 'svg')
183
183
  svg.setAttribute('viewBox', '0 0 24 24')
184
- svg.setAttribute('width', '22')
185
- svg.setAttribute('height', '22')
184
+ svg.setAttribute('width', '16')
185
+ svg.setAttribute('height', '16')
186
186
  svg.setAttribute('fill', 'none')
187
187
  svg.setAttribute('stroke', 'currentColor')
188
- svg.setAttribute('stroke-width', '1.7')
188
+ svg.setAttribute('stroke-width', '2')
189
189
  svg.setAttribute('stroke-linecap', 'round')
190
190
  svg.setAttribute('stroke-linejoin', 'round')
191
191
  svg.setAttribute('aria-hidden', 'true')
@@ -193,15 +193,15 @@ function quotaIcon(doc) {
193
193
  const dial = doc.createElementNS(namespace, 'circle')
194
194
  dial.setAttribute('cx', '12')
195
195
  dial.setAttribute('cy', '12')
196
- dial.setAttribute('r', '9')
196
+ dial.setAttribute('r', '10')
197
197
  const ticks = doc.createElementNS(namespace, 'path')
198
- ticks.setAttribute('d', 'M12 2.7v2.1M12 19.2v2.1M2.7 12h2.1M19.2 12h2.1M5.43 5.43l1.49 1.49M17.08 17.08l1.49 1.49M18.57 5.43l-1.49 1.49M6.92 17.08l-1.49 1.49')
198
+ ticks.setAttribute('d', 'M12 2.4v1.6M12 20v1.6M2.4 12h1.6M20 12h1.6M5.2 5.2l1.1 1.1M18.8 18.8l-1.1-1.1M18.8 5.2l-1.1 1.1M5.2 18.8l1.1-1.1')
199
199
  const needle = doc.createElementNS(namespace, 'path')
200
200
  needle.setAttribute('d', 'M12 12l4.5-4')
201
201
  const hub = doc.createElementNS(namespace, 'circle')
202
202
  hub.setAttribute('cx', '12')
203
203
  hub.setAttribute('cy', '12')
204
- hub.setAttribute('r', '1.2')
204
+ hub.setAttribute('r', '1.4')
205
205
  hub.setAttribute('fill', 'currentColor')
206
206
  hub.setAttribute('stroke', 'none')
207
207
  svg.append(dial, ticks, needle, hub)
@@ -304,7 +304,7 @@ export function mountQuotaCard({
304
304
  container.dataset.dshQuotaWatchCard = ''
305
305
  container.dataset.dshPlugin = 'quota-watch'
306
306
  container.dataset.dshPart = 'sidebar-card'
307
- const style = text(doc, 'style', '', STYLE_TEXT)
307
+ let style = text(doc, 'style', '', STYLE_TEXT)
308
308
  const card = doc.createElement('section')
309
309
  card.className = 'dqw-card'
310
310
  card.setAttribute('aria-label', copy.title)
@@ -325,6 +325,34 @@ export function mountQuotaCard({
325
325
  body.append(lastUpdated)
326
326
  card.append(body)
327
327
  container.append(style, card, railTrigger)
328
+ // The card carries its stylesheet in a <style> tag next to its DOM. External
329
+ // actors (browser extensions, aggressive theming) have been observed to strip
330
+ // or neuter foreign style tags while the page's styles churn (theme switches,
331
+ // plugin reloads, service restarts); the card then renders unstyled until a
332
+ // full page refresh. Two defenses:
333
+ // 1. mirror the rules into a constructable stylesheet (adoptedStyleSheets),
334
+ // which those actors do not touch;
335
+ // 2. re-create the tag on every render pass that finds it dead or missing.
336
+ const supportsAdopted =
337
+ Array.isArray(doc.adoptedStyleSheets) && typeof win.CSSStyleSheet === 'function'
338
+ let adoptedSheet = null
339
+ if (supportsAdopted) {
340
+ try {
341
+ adoptedSheet = new win.CSSStyleSheet()
342
+ adoptedSheet.replaceSync(STYLE_TEXT)
343
+ doc.adoptedStyleSheets = [...doc.adoptedStyleSheets, adoptedSheet]
344
+ } catch {
345
+ adoptedSheet = null
346
+ }
347
+ }
348
+ let styleAlive = () => doc.contains(style) && style.sheet !== null && style.sheet.cssRules.length > 0
349
+ let ensureStyleAlive = () => {
350
+ if (styleAlive()) return
351
+ const fresh = text(doc, 'style', '', STYLE_TEXT)
352
+ if (doc.contains(style)) style.replaceWith(fresh)
353
+ else container.prepend(fresh)
354
+ style = fresh
355
+ }
328
356
  // Details and collapsed-rail summaries open in a viewport-level panel.
329
357
  const pop = doc.createElement('div')
330
358
  pop.className = 'dqw-pop'
@@ -620,6 +648,7 @@ export function mountQuotaCard({
620
648
  }
621
649
 
622
650
  const renderAll = () => {
651
+ ensureStyleAlive()
623
652
  const providers = snapshot?.providers ?? []
624
653
  const glm = providers.find((provider) => provider?.key === 'glm')
625
654
  const copilot = providers.find((provider) => provider?.key === 'copilot')
@@ -806,6 +835,9 @@ export function mountQuotaCard({
806
835
  pop.removeEventListener('click', onPopClick)
807
836
  container.remove()
808
837
  pop.remove()
838
+ if (adoptedSheet !== null && Array.isArray(doc.adoptedStyleSheets)) {
839
+ doc.adoptedStyleSheets = doc.adoptedStyleSheets.filter((sheet) => sheet !== adoptedSheet)
840
+ }
809
841
  }
810
842
  }
811
843