@aiquants/markdown-explorer 0.0.0-stage → 0.2.0

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.
Files changed (78) hide show
  1. package/CHANGELOG.md +121 -0
  2. package/LICENSE +21 -0
  3. package/README.md +606 -2
  4. package/dist/ExplorerSplit-D2plJRRe.cjs +1 -0
  5. package/dist/ExplorerSplit-DB37U7Kh.js +1362 -0
  6. package/dist/MultiLayout-DCPDu1-l.js +909 -0
  7. package/dist/MultiLayout-UyWxs6Xy.cjs +1 -0
  8. package/dist/TreeLayout-BoyFelK2.js +22 -0
  9. package/dist/TreeLayout-CLWbWjOc.cjs +1 -0
  10. package/dist/client/MarkdownExplorer.d.cts +30 -0
  11. package/dist/client/MarkdownExplorer.d.ts +30 -0
  12. package/dist/client/storage.d.cts +11 -0
  13. package/dist/client/storage.d.ts +11 -0
  14. package/dist/core/config.d.cts +77 -0
  15. package/dist/core/config.d.ts +77 -0
  16. package/dist/core/contracts.d.cts +155 -0
  17. package/dist/core/contracts.d.ts +155 -0
  18. package/dist/core/geometry.d.cts +51 -0
  19. package/dist/core/geometry.d.ts +51 -0
  20. package/dist/core/labels.d.cts +180 -0
  21. package/dist/core/labels.d.ts +180 -0
  22. package/dist/core/location.d.cts +82 -0
  23. package/dist/core/location.d.ts +82 -0
  24. package/dist/core/paths.d.cts +17 -0
  25. package/dist/core/paths.d.ts +17 -0
  26. package/dist/core/revalidation.d.cts +2 -0
  27. package/dist/core/revalidation.d.ts +2 -0
  28. package/dist/core/theme.d.cts +6 -0
  29. package/dist/core/theme.d.ts +6 -0
  30. package/dist/core/treeSettings.d.cts +48 -0
  31. package/dist/core/treeSettings.d.ts +48 -0
  32. package/dist/core/viewSettings.d.cts +15 -0
  33. package/dist/core/viewSettings.d.ts +15 -0
  34. package/dist/dataRoute-Bkfwfp1R.js +194 -0
  35. package/dist/dataRoute-CKbdwkQ7.cjs +1 -0
  36. package/dist/index-BfuAu2GV.js +2107 -0
  37. package/dist/index-Cx3-jSna.cjs +3 -0
  38. package/dist/index.cjs +1 -0
  39. package/dist/index.d.cts +12 -0
  40. package/dist/index.d.ts +12 -0
  41. package/dist/index.js +44 -0
  42. package/dist/location-Oo21lTyV.cjs +1 -0
  43. package/dist/location-fVl2R1ee.js +395 -0
  44. package/dist/sanitizeWorker.cjs +1 -0
  45. package/dist/sanitizeWorker.js +281 -0
  46. package/dist/server/anonymous.d.cts +10 -0
  47. package/dist/server/anonymous.d.ts +10 -0
  48. package/dist/server/createMarkdownExplorer.d.cts +12 -0
  49. package/dist/server/createMarkdownExplorer.d.ts +12 -0
  50. package/dist/server/errors.d.cts +26 -0
  51. package/dist/server/errors.d.ts +26 -0
  52. package/dist/server/fileSystemSource.d.cts +38 -0
  53. package/dist/server/fileSystemSource.d.ts +38 -0
  54. package/dist/server/ports.d.cts +107 -0
  55. package/dist/server/ports.d.ts +107 -0
  56. package/dist/server/sanitizeProtocol.d.cts +17 -0
  57. package/dist/server/sanitizeProtocol.d.ts +17 -0
  58. package/dist/server/sanitizer.d.cts +51 -0
  59. package/dist/server/sanitizer.d.ts +51 -0
  60. package/dist/server/workerPool.d.cts +42 -0
  61. package/dist/server/workerPool.d.ts +42 -0
  62. package/dist/server.cjs +1 -0
  63. package/dist/server.d.cts +7 -0
  64. package/dist/server.d.ts +7 -0
  65. package/dist/server.js +1253 -0
  66. package/dist/storage-Bkb2Axdi.cjs +1 -0
  67. package/dist/storage-DA4ItHzl.js +60 -0
  68. package/dist/storage.cjs +1 -0
  69. package/dist/storage.d.cts +1 -0
  70. package/dist/storage.d.ts +1 -0
  71. package/dist/storage.js +4 -0
  72. package/dist/styles/markdown-explorer.css +3 -0
  73. package/docs/behavior/dom-hooks.md +95 -0
  74. package/docs/behavior/layout.md +215 -0
  75. package/docs/behavior/localization.md +48 -0
  76. package/docs/behavior/security.md +138 -0
  77. package/docs/behavior/url-contract.md +135 -0
  78. package/package.json +141 -3
@@ -0,0 +1 @@
1
+ "use strict";const f=/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u,s=t=>{if(typeof t!="string"||!f.test(t))throw new RangeError(`[markdown-explorer] storageNamespace must be 1-64 letters, digits, ".", "_" or "-" starting with a letter or digit; got ${JSON.stringify(t)}`)},c=t=>(s(t),Object.freeze({treeSettings:`${t}:tree-settings`,multiLayout:`${t}:multi-layout`,scope:`${t}:scope`,treeExpansion:r=>`${t}:tree-expansion:${r}`})),l=(t,r,e)=>{console.warn(`[markdown-explorer] could not ${t} localStorage key ${JSON.stringify(r)}; continuing without it.`,e)},i=()=>{if(typeof window>"u")return null;try{return window.localStorage}catch{return null}},u=t=>{const r=i();if(r===null)return null;try{return r.getItem(t)}catch(e){return l("read",t,e),null}},g=(t,r)=>{const e=i();if(e!==null)try{e.setItem(t,r)}catch(o){l("write",t,o)}},S=t=>{s(t);const r=i();if(r===null)return;const e=`${t}:`;try{const o=[];for(let n=0;n<r.length;n+=1){const a=r.key(n);a?.startsWith(e)===!0&&o.push(a)}for(const n of o)r.removeItem(n)}catch(o){l("clear",`${t}:*`,o)}},y=(t,r)=>{const e=c(t).scope,o=u(e);return o===r?!1:(S(t),g(e,r),o!==null)};exports.assertStorageNamespace=s;exports.clearExplorerStorage=S;exports.readStorage=u;exports.storageKeys=c;exports.syncStorageScope=y;exports.writeStorage=g;
@@ -0,0 +1,60 @@
1
+ const c = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u, a = (t) => {
2
+ if (typeof t != "string" || !c.test(t))
3
+ throw new RangeError(`[markdown-explorer] storageNamespace must be 1-64 letters, digits, ".", "_" or "-" starting with a letter or digit; got ${JSON.stringify(t)}`);
4
+ }, u = (t) => (a(t), Object.freeze({
5
+ treeSettings: `${t}:tree-settings`,
6
+ multiLayout: `${t}:multi-layout`,
7
+ scope: `${t}:scope`,
8
+ treeExpansion: (r) => `${t}:tree-expansion:${r}`
9
+ })), s = (t, r, e) => {
10
+ console.warn(`[markdown-explorer] could not ${t} localStorage key ${JSON.stringify(r)}; continuing without it.`, e);
11
+ }, l = () => {
12
+ if (typeof window > "u") return null;
13
+ try {
14
+ return window.localStorage;
15
+ } catch {
16
+ return null;
17
+ }
18
+ }, g = (t) => {
19
+ const r = l();
20
+ if (r === null) return null;
21
+ try {
22
+ return r.getItem(t);
23
+ } catch (e) {
24
+ return s("read", t, e), null;
25
+ }
26
+ }, f = (t, r) => {
27
+ const e = l();
28
+ if (e !== null)
29
+ try {
30
+ e.setItem(t, r);
31
+ } catch (o) {
32
+ s("write", t, o);
33
+ }
34
+ }, d = (t) => {
35
+ a(t);
36
+ const r = l();
37
+ if (r === null) return;
38
+ const e = `${t}:`;
39
+ try {
40
+ const o = [];
41
+ for (let n = 0; n < r.length; n += 1) {
42
+ const i = r.key(n);
43
+ i?.startsWith(e) === !0 && o.push(i);
44
+ }
45
+ for (const n of o) r.removeItem(n);
46
+ } catch (o) {
47
+ s("clear", `${t}:*`, o);
48
+ }
49
+ }, w = (t, r) => {
50
+ const e = u(t).scope, o = g(e);
51
+ return o === r ? !1 : (d(t), f(e, r), o !== null);
52
+ };
53
+ export {
54
+ a,
55
+ w as b,
56
+ d as c,
57
+ g as r,
58
+ u as s,
59
+ f as w
60
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./storage-Bkb2Axdi.cjs");exports.clearExplorerStorage=e.clearExplorerStorage;
@@ -0,0 +1 @@
1
+ export { clearExplorerStorage } from "./client/storage.cjs";
@@ -0,0 +1 @@
1
+ export { clearExplorerStorage } from "./client/storage.js";
@@ -0,0 +1,4 @@
1
+ import { c as o } from "./storage-DA4ItHzl.js";
2
+ export {
3
+ o as clearExplorerStorage
4
+ };
@@ -0,0 +1,3 @@
1
+ /*! tailwindcss v4.3.0 | MIT License | https://tailwindcss.com */
2
+ /*! @aiquants/markdown-explorer — component styles, layered in `components`. The layer order is pinned first (theme, base, components, utilities, as Tailwind CSS declares it), so a Tailwind host's preflight in `base` cannot override the explorer when this sheet is the first to name a layer. */
3
+ @layer theme,base;@layer components{.aqmx-root{--aqmx-space-1:.0625rem;--aqmx-space-2:.125rem;--aqmx-space-3:.1875rem;--aqmx-space-4:.3125rem;--aqmx-space-5:.5rem;--aqmx-space-6:.8125rem;--aqmx-space-7:1.3125rem;--aqmx-space-8:2.125rem;--aqmx-space-9:3.4375rem;--aqmx-control:2.125rem;--aqmx-focus-ring-width:var(--aqmx-space-2);--aqmx-focus-ring-offset:var(--aqmx-space-2);--aqmx-icon:1rem;--aqmx-radius-s:.3125rem;--aqmx-radius-m:.5rem;--aqmx-radius-l:.8125rem;--aqmx-duration-1:89ms;--aqmx-duration-2:.144s;--aqmx-duration-3:.233s;--aqmx-duration-4:.377s;--aqmx-delay-progress:.233s;--aqmx-ease:cubic-bezier(.2, 0, 0, 1);--aqmx-font-size-body:.8125rem;--aqmx-font-size-title:1rem;--aqmx-font-size-display:1.3125rem;--aqmx-line-height-compact:1.382;--aqmx-line-height-prose:1.618;--aqmx-column-measure:40ch;--aqmx-surface:#fff;--aqmx-surface-raised:#f8fafc;--aqmx-surface-sunken:#f1f5f9;--aqmx-border:#cbd5e1;--aqmx-border-strong:#94a3b8;--aqmx-text:#0f172a;--aqmx-text-muted:#475569;--aqmx-accent:#1d4ed8;--aqmx-accent-surface:#dbeafe;--aqmx-tree-line:#64748b;--aqmx-tree-row-tint:#0f172a0f;--aqmx-tree-row-accent-tint:#2563eb29;--aqmx-focus:#2563eb;--aqmx-info-surface:#eff6ff;--aqmx-info-border:#93c5fd;--aqmx-info-text:#1e3a8a;--aqmx-warning-surface:#fffbeb;--aqmx-warning-border:#fcd34d;--aqmx-warning-text:#78350f;--aqmx-danger-surface:#fef2f2;--aqmx-danger-border:#fca5a5;--aqmx-danger-text:#7f1d1d;--aqmx-shadow:0 .0625rem .1875rem #0f172a21, 0 .3125rem .8125rem #0f172a14}.aqmx-root[data-aqmx-theme=dark]{--aqmx-surface:#0f172a;--aqmx-surface-raised:#1e293b;--aqmx-surface-sunken:#020617;--aqmx-border:#334155;--aqmx-border-strong:#64748b;--aqmx-text:#e2e8f0;--aqmx-text-muted:#94a3b8;--aqmx-accent:#93c5fd;--aqmx-accent-surface:#1e3a8a;--aqmx-tree-line:#94a3b8;--aqmx-tree-row-tint:#e2e8f00f;--aqmx-tree-row-accent-tint:#2563eb70;--aqmx-focus:#60a5fa;--aqmx-info-surface:#172554;--aqmx-info-border:#1d4ed8;--aqmx-info-text:#bfdbfe;--aqmx-warning-surface:#422006;--aqmx-warning-border:#a16207;--aqmx-warning-text:#fde68a;--aqmx-danger-surface:#450a0a;--aqmx-danger-border:#b91c1c;--aqmx-danger-text:#fecaca;--aqmx-shadow:0 .0625rem .1875rem #0000008c, 0 .3125rem .8125rem #00000057}@media (pointer:coarse){.aqmx-root{--aqmx-control:2.75rem}}@media (forced-colors:active){.aqmx-root.aqmx-root[data-aqmx-theme]{--aqmx-tree-line:CanvasText}}@media (prefers-reduced-motion:reduce){.aqmx-root{--aqmx-duration-1:0s;--aqmx-duration-2:0s;--aqmx-duration-3:0s;--aqmx-duration-4:0s}}.aqmx-root{block-size:100%;min-block-size:0;inline-size:100%;min-inline-size:0;color:var(--aqmx-text);background:var(--aqmx-surface);font-size:var(--aqmx-font-size-body);line-height:var(--aqmx-line-height-compact);flex-direction:column;display:flex;position:relative}.aqmx-root *,.aqmx-root :before,.aqmx-root :after{box-sizing:border-box}.aqmx-icon-button{inline-size:var(--aqmx-control);block-size:var(--aqmx-control);border-radius:var(--aqmx-radius-m);color:var(--aqmx-text-muted);cursor:pointer;transition:background-color var(--aqmx-duration-2) var(--aqmx-ease), color var(--aqmx-duration-2) var(--aqmx-ease);background:0 0;border:0;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.aqmx-icon-button:hover{color:var(--aqmx-text);background:var(--aqmx-surface-sunken)}.aqmx-icon-button[aria-pressed=true],.aqmx-icon-button[aria-expanded=true]{color:var(--aqmx-accent);background:var(--aqmx-accent-surface)}.aqmx-icon-button:disabled{opacity:.382;cursor:not-allowed}.aqmx-icon-button[aria-disabled=true]{cursor:not-allowed}.aqmx-icon-button[aria-disabled=true]:hover{color:var(--aqmx-text-muted);background:0 0}.aqmx-icon-button>svg{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon)}.aqmx-icon-button[aria-disabled=true]>svg{opacity:.382}.aqmx-root :focus-visible{outline:var(--aqmx-focus-ring-width) solid var(--aqmx-focus);outline-offset:var(--aqmx-focus-ring-offset)}.aqmx-callout{gap:var(--aqmx-space-5);padding:var(--aqmx-space-6);border:var(--aqmx-space-1) solid var(--aqmx-info-border);border-radius:var(--aqmx-radius-l);color:var(--aqmx-info-text);background:var(--aqmx-info-surface);line-height:var(--aqmx-line-height-prose);display:grid}.aqmx-callout[data-aqmx-tone=warning]{border-color:var(--aqmx-warning-border);color:var(--aqmx-warning-text);background:var(--aqmx-warning-surface)}.aqmx-callout[data-aqmx-tone=danger]{border-color:var(--aqmx-danger-border);color:var(--aqmx-danger-text);background:var(--aqmx-danger-surface)}.aqmx-callout-title{font-size:var(--aqmx-font-size-title);font-weight:600;line-height:var(--aqmx-line-height-compact);margin:0}.aqmx-callout-body{margin:0}.aqmx-callout-action{min-block-size:var(--aqmx-control);padding-inline:var(--aqmx-space-6);border:var(--aqmx-space-1) solid currentColor;border-radius:var(--aqmx-radius-m);color:inherit;font:inherit;cursor:pointer;background:0 0;justify-self:start;align-items:center;font-weight:600;text-decoration:none;display:inline-flex}.aqmx-progress{z-index:2;block-size:var(--aqmx-space-3);pointer-events:none;position:absolute;inset-block-start:0;inset-inline:0;overflow:hidden}.aqmx-progress:before{content:"";background:var(--aqmx-accent);inline-size:38.1966%;animation:aqmx-progress calc(var(--aqmx-duration-4) * 3) var(--aqmx-ease) infinite;position:absolute;inset-block:0}@keyframes aqmx-progress{0%{inset-inline-start:-38.1966%}to{inset-inline-start:100%}}.aqmx-empty{place-content:center;justify-items:center;gap:var(--aqmx-space-6);block-size:100%;padding:var(--aqmx-space-8);color:var(--aqmx-text-muted);text-align:center;line-height:var(--aqmx-line-height-prose);display:grid}.aqmx-empty-title{color:var(--aqmx-text);font-size:var(--aqmx-font-size-title);margin:0;font-weight:600}.aqmx-skeleton{gap:var(--aqmx-space-6);padding:var(--aqmx-space-7);display:grid}.aqmx-skeleton>span{block-size:var(--aqmx-space-6);border-radius:var(--aqmx-radius-s);background:var(--aqmx-surface-sunken);animation:aqmx-pulse calc(var(--aqmx-duration-4) * 4) ease-in-out infinite;display:block}.aqmx-skeleton>span:nth-child(3n+1){inline-size:61.8034%}.aqmx-skeleton>span:nth-child(3n+2){inline-size:100%}.aqmx-skeleton>span:nth-child(3n){inline-size:85.4102%}@keyframes aqmx-pulse{50%{opacity:.618}}.aqmx-sr-only{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;block-size:1px;inline-size:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.aqmx-toast{z-index:60;max-inline-size:min(34rem, calc(100% - var(--aqmx-space-8)));padding:var(--aqmx-space-5) var(--aqmx-space-6);border:var(--aqmx-space-1) solid var(--aqmx-warning-border);border-radius:var(--aqmx-radius-m);color:var(--aqmx-warning-text);background:var(--aqmx-warning-surface);box-shadow:var(--aqmx-shadow);position:absolute;inset-block-start:var(--aqmx-space-6);inset-inline-end:var(--aqmx-space-7)}@media (prefers-reduced-motion:reduce){.aqmx-progress:before,.aqmx-skeleton>span{animation:none}}@media (forced-colors:active){.aqmx-icon-button[aria-pressed=true],.aqmx-icon-button[aria-expanded=true]{forced-color-adjust:none;color:highlighttext;background:highlight}.aqmx-icon-button[aria-disabled=true]{color:graytext}.aqmx-root :focus-visible{outline-color:highlight}.aqmx-progress:before{forced-color-adjust:none;background:highlight}.aqmx-skeleton>span{forced-color-adjust:none;background:graytext}}.aqmx-explorer{gap:var(--aqmx-space-5);block-size:100%;min-block-size:0;min-inline-size:0;padding:var(--aqmx-space-4);color:var(--aqmx-text);background:var(--aqmx-surface);flex-direction:column;display:flex;position:relative;overflow-y:auto}.aqmx-explorer-header{align-items:center;gap:var(--aqmx-space-4);min-block-size:var(--aqmx-control);flex-wrap:wrap;flex:none;display:flex}.aqmx-explorer-source{min-inline-size:min(100%,9rem);block-size:var(--aqmx-control);padding-inline:var(--aqmx-space-5);border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-m);color:var(--aqmx-text);background:var(--aqmx-surface-raised);font:inherit;text-overflow:ellipsis;cursor:pointer;flex:9rem}.aqmx-explorer-actions{justify-content:flex-end;align-items:center;gap:var(--aqmx-space-2);flex-wrap:wrap;flex:0 auto;min-inline-size:0;margin-inline-start:auto;display:flex}.aqmx-explorer-body{gap:var(--aqmx-space-5);flex-direction:column;flex:1 1 0;min-block-size:0;min-inline-size:0;display:flex;position:relative}.aqmx-labelled-button{gap:var(--aqmx-space-2);min-inline-size:var(--aqmx-control);inline-size:auto;padding-inline:var(--aqmx-space-3);white-space:nowrap;flex:0 auto;display:inline-flex}.aqmx-labelled-button>svg{flex:none}.aqmx-labelled-button>span{text-overflow:ellipsis;min-inline-size:0;overflow:hidden}.aqmx-explorer-refresh-failure,.aqmx-explorer-expand-all-stop{padding:var(--aqmx-space-4) var(--aqmx-space-5);border:var(--aqmx-space-1) solid;border-radius:var(--aqmx-radius-m);line-height:var(--aqmx-line-height-compact);overflow-wrap:anywhere;flex:none;margin:0}.aqmx-explorer-refresh-failure{border-color:var(--aqmx-danger-border);color:var(--aqmx-danger-text);background:var(--aqmx-danger-surface)}.aqmx-explorer-expand-all-stop{border-color:var(--aqmx-info-border);color:var(--aqmx-info-text);background:var(--aqmx-info-surface)}.aqmx-explorer-actions>.aqmx-labelled-button[data-aqmx-busy=true]>svg{animation:aqmx-explorer-spin calc(var(--aqmx-duration-4) * 3) linear infinite}@keyframes aqmx-explorer-spin{to{rotate:1turn}}.aqmx-explorer-actions>.aqmx-icon-button[aria-busy=true]{cursor:progress}.aqmx-explorer-menu{z-index:50;gap:var(--aqmx-space-1);max-block-size:100%;inline-size:min(21rem,100%);padding:var(--aqmx-space-3);border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-m);background:var(--aqmx-surface-raised);box-shadow:var(--aqmx-shadow);display:grid;position:absolute;inset-block-start:0;inset-inline-end:0;overflow-y:auto}.aqmx-explorer-menu-item{align-items:center;gap:var(--aqmx-space-5);inline-size:100%;min-block-size:var(--aqmx-control);padding-inline:var(--aqmx-space-5);border-radius:var(--aqmx-radius-s);color:var(--aqmx-text);font:inherit;text-align:start;cursor:pointer;transition:background-color var(--aqmx-duration-1) var(--aqmx-ease);background:0 0;border:0;display:flex}.aqmx-explorer-menu-item:hover,.aqmx-explorer-menu-item:focus-visible{background:var(--aqmx-surface-sunken)}.aqmx-explorer-menu-icon{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon);color:var(--aqmx-text-muted);flex:none}.aqmx-explorer-menu-name{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-inline-size:0;overflow:hidden}.aqmx-explorer-menu-value{color:var(--aqmx-accent);font-variant-numeric:tabular-nums;flex:none;font-weight:600}.aqmx-explorer-notices{gap:var(--aqmx-space-5);flex:none;display:grid}.aqmx-explorer-tree-area{min-block-size:calc(var(--aqmx-control) * 3);flex-direction:column;flex:1 1 0;min-inline-size:0;display:flex;position:relative}.aqmx-explorer-tree{flex:1 1 0;min-block-size:0;position:relative}.aqmx-explorer-tree-view{block-size:100%}.aqmx-explorer-tree-progress{z-index:1;align-items:center;gap:var(--aqmx-space-2) var(--aqmx-space-4);max-inline-size:calc(100% - var(--aqmx-space-5) * 3);padding:var(--aqmx-space-3) var(--aqmx-space-5);border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-m);color:var(--aqmx-text-muted);background:var(--aqmx-surface-raised);box-shadow:var(--aqmx-shadow);line-height:var(--aqmx-line-height-compact);overflow-wrap:anywhere;pointer-events:none;animation:aqmx-appear var(--aqmx-duration-2) ease-out var(--aqmx-delay-progress) both;flex-wrap:wrap;margin:0;display:flex;position:absolute;inset-block-end:var(--aqmx-space-9);inset-inline-start:var(--aqmx-space-5)}.aqmx-explorer-tree-progress-icon{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon);animation:aqmx-explorer-spin calc(var(--aqmx-duration-4) * 3) linear infinite;flex:none}@keyframes aqmx-appear{0%{opacity:0}}.aqmx-explorer-tree-probe{inline-size:0;block-size:var(--aqmx-control);visibility:hidden;pointer-events:none;color:var(--aqmx-tree-line);forced-color-adjust:none;position:absolute;inset-block-start:0;inset-inline-start:0;overflow:hidden}.aqmx-explorer-loading{gap:var(--aqmx-space-5);padding:var(--aqmx-space-6) var(--aqmx-space-5);color:var(--aqmx-text-muted);display:grid}.aqmx-explorer-loading-text{margin:0}.aqmx-explorer-loading>.aqmx-skeleton{padding:0}.aqmx-explorer-tree-message{padding:var(--aqmx-space-5);overflow-y:auto}.aqmx-explorer-empty-icon{inline-size:var(--aqmx-space-8);block-size:var(--aqmx-space-8);color:var(--aqmx-border-strong)}.aqmx-explorer-empty-body{margin:0}.aqmx-explorer-tree [role=treeitem]{border-radius:var(--aqmx-radius-s);color:var(--aqmx-text);transition:background-color var(--aqmx-duration-1) var(--aqmx-ease)}.aqmx-explorer-tree [role=treeitem]:hover{color:var(--aqmx-text);background:var(--aqmx-tree-row-tint)}.aqmx-explorer-tree [role=treeitem][data-entry-item-selected=true]{color:var(--aqmx-text);background:var(--aqmx-tree-row-tint);box-shadow:inset var(--aqmx-space-2) 0 0 currentColor}.aqmx-explorer-tree [role=treeitem][data-entry-selected=true],.aqmx-explorer-tree [role=treeitem][data-aqmx-open=true]{color:var(--aqmx-accent);background:var(--aqmx-tree-row-accent-tint)}.aqmx-explorer-tree [role=treeitem]:focus-visible{outline-offset:calc(-1 * var(--aqmx-focus-ring-width))}.aqmx-explorer-tree [role=treeitem][data-entry-item-selected=true]:focus-visible{box-shadow:inset calc(var(--aqmx-space-2) + var(--aqmx-focus-ring-width)) 0 0 currentColor}.aqmx-explorer-tree [role=treeitem][data-aqmx-open=true]{font-weight:600}.aqmx-explorer-tree [role=treeitem][data-aqmx-children=loading]{animation:aqmx-pulse calc(var(--aqmx-duration-4) * 4) ease-in-out infinite}.aqmx-explorer-tree [role=treeitem][data-aqmx-loop]{color:var(--aqmx-text-muted);font-style:italic}.aqmx-explorer-folder-failed{--aqmx-folder-name-floor:3em;align-items:center;min-inline-size:0;max-inline-size:100%;display:flex}.aqmx-explorer-folder-name{flex:10000 1 var(--aqmx-folder-name-floor);text-overflow:ellipsis;white-space:nowrap;min-inline-size:0;max-inline-size:max-content;overflow:hidden}.aqmx-explorer-folder-error{min-inline-size:0;color:var(--aqmx-danger-text);text-indent:var(--aqmx-space-4);text-overflow:ellipsis;white-space:nowrap;flex:1 1 0;overflow:hidden}.aqmx-explorer-folder-retry{flex:1000000 0 calc(var(--aqmx-icon) + (var(--aqmx-space-3) + var(--aqmx-space-1)) * 2);min-inline-size:calc(var(--aqmx-icon) + (var(--aqmx-space-3) + var(--aqmx-space-1)) * 2);max-inline-size:max-content;min-block-size:var(--aqmx-control);padding:0 var(--aqmx-space-3);border:var(--aqmx-space-1) solid var(--aqmx-danger-border);border-radius:var(--aqmx-radius-s);color:var(--aqmx-danger-text);background:var(--aqmx-danger-surface);font:inherit;line-height:var(--aqmx-line-height-compact);cursor:pointer;justify-content:center;align-items:center;margin-inline-start:var(--aqmx-space-4);display:inline-flex;overflow:hidden}.aqmx-explorer-folder-retry:focus-visible{outline-offset:calc(-1 * (var(--aqmx-focus-ring-width) + var(--aqmx-space-1)))}.aqmx-explorer-folder-retry-icon{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon);flex:none}.aqmx-explorer-folder-retry-text{min-inline-size:0;text-indent:var(--aqmx-space-3);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.aqmx-explorer-tray{gap:var(--aqmx-space-4);min-block-size:calc(var(--aqmx-control) + (var(--aqmx-space-5) + var(--aqmx-space-1)) * 2);padding:var(--aqmx-space-5);border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-m);background:var(--aqmx-surface-raised);box-shadow:var(--aqmx-shadow);flex-direction:column;flex:0 auto;display:flex;overflow-y:auto}.aqmx-explorer-tray-header{justify-content:space-between;align-items:center;gap:var(--aqmx-space-5);flex:none;display:flex}.aqmx-explorer-tray-title{margin:0;font-weight:600}.aqmx-explorer-tray-button{min-block-size:var(--aqmx-control);padding-inline:var(--aqmx-space-5);border-radius:var(--aqmx-radius-m);color:var(--aqmx-accent);font:inherit;cursor:pointer;background:0 0;border:0;flex:none;font-weight:600}.aqmx-explorer-tray-button:hover{background:var(--aqmx-surface-sunken)}.aqmx-explorer-tray-list{gap:var(--aqmx-space-1);min-block-size:var(--aqmx-control);max-block-size:calc(var(--aqmx-control) * 3);flex:0 auto;margin:0;padding:0;list-style:none;display:grid;overflow-y:auto}.aqmx-explorer-tray-item{align-items:center;gap:var(--aqmx-space-4);display:flex}.aqmx-explorer-tray-name{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-inline-size:0;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow:hidden}.aqmx-explorer-tray-open{min-block-size:var(--aqmx-control);padding-inline:var(--aqmx-space-6);border-radius:var(--aqmx-radius-m);color:var(--aqmx-surface);background:var(--aqmx-accent);font:inherit;cursor:pointer;border:0;flex:none;font-weight:600}@media (forced-colors:active){.aqmx-explorer-tree [role=treeitem][data-entry-item-selected=true],.aqmx-explorer-tree [role=treeitem][data-entry-selected=true],.aqmx-explorer-tree [role=treeitem][data-aqmx-open=true],.aqmx-explorer-menu-item[aria-checked=true]{forced-color-adjust:none;color:highlighttext;background:highlight}.aqmx-explorer-menu-item[aria-checked=true] *,.aqmx-explorer-tree [role=treeitem][data-entry-item-selected=true] *,.aqmx-explorer-tree [role=treeitem][data-entry-selected=true] *,.aqmx-explorer-tree [role=treeitem][data-aqmx-open=true] *{color:inherit!important}.aqmx-explorer-tree [role=treeitem][data-entry-item-selected=true]:focus-visible,.aqmx-explorer-tree [role=treeitem][data-entry-selected=true]:focus-visible,.aqmx-explorer-tree [role=treeitem][data-aqmx-open=true]:focus-visible{outline-color:highlighttext}.aqmx-explorer-tray-button,.aqmx-explorer-tray-open,.aqmx-explorer-tray-remove{border:var(--aqmx-space-1) solid ButtonText}}@media (prefers-reduced-motion:reduce){.aqmx-explorer-actions>.aqmx-labelled-button[data-aqmx-busy=true]>svg,.aqmx-explorer-tree-progress-icon,.aqmx-explorer-tree [role=treeitem][data-aqmx-children=loading]{animation:none}.aqmx-explorer-tree-view{transition-duration:0s!important}}.aqmx-document{contain:layout paint;--aqmd-toolbar-inset-block-start:calc(var(--aqmx-focus-ring-width) + var(--aqmx-focus-ring-offset));block-size:100%;min-block-size:0;inline-size:100%;min-inline-size:0;color:var(--aqmx-text);background:var(--aqmx-surface);flex-direction:column;display:flex;position:relative;overflow:hidden}.aqmx-document [data-theme]{background-color:#0000!important}.aqmx-document .markdown-body :is(sup,sub) a{outline-offset:var(--aqmx-focus-ring-offset)!important}.aqmx-document *{scroll-margin:calc(var(--aqmx-focus-ring-width) + var(--aqmx-focus-ring-offset))}.aqmx-document[aria-busy=true]>:not(.aqmx-progress){opacity:.618;transition:opacity var(--aqmx-duration-3) var(--aqmx-ease)}.aqmx-document-viewer{flex:1 1 0;min-block-size:0;min-inline-size:0}.aqmx-document-notice{margin:var(--aqmx-space-6) var(--aqmx-space-7) 0;overflow-wrap:anywhere;flex:none}.aqmx-document-failure{min-block-size:0;padding:var(--aqmx-space-7);flex:1 1 0;overflow:auto}.aqmx-document-file{flex-direction:column;flex:1 1 0;min-block-size:0;min-inline-size:0;display:flex}.aqmx-document-toolbar{align-items:center;gap:var(--aqmx-space-3);padding:var(--aqmx-space-3) var(--aqmx-space-6);border-block-end:var(--aqmx-space-1) solid var(--aqmx-border);background:var(--aqmx-surface-raised);flex-wrap:wrap;flex:none;display:flex}.aqmx-document-file-icon{color:var(--aqmx-text-muted);flex:none;display:inline-flex}.aqmx-document-file-icon>svg{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon)}.aqmx-document-title{text-overflow:ellipsis;white-space:nowrap;flex:1 1 0;min-inline-size:0;font-weight:600;overflow:hidden}.aqmx-document-level{min-inline-size:var(--aqmx-space-9);color:var(--aqmx-text-muted);font-variant-numeric:tabular-nums;text-align:center;flex:none}.aqmx-document-canvas{min-block-size:0;padding:var(--aqmx-space-7);background-color:var(--aqmx-surface-sunken);background-image:radial-gradient(var(--aqmx-border) var(--aqmx-space-1), transparent var(--aqmx-space-1));background-size:var(--aqmx-space-6) var(--aqmx-space-6);flex:1 1 0;grid-template:minmax(0,1fr)/minmax(0,1fr);display:grid;overflow:auto}.aqmx-document-image{margin:auto;display:block}.aqmx-document-image[data-aqmx-image-mode=fit]{object-fit:contain;max-block-size:100%;max-inline-size:100%}.aqmx-document-image[data-aqmx-image-mode=level]{max-block-size:none;max-inline-size:none}.aqmx-document-stage{min-block-size:0;padding:var(--aqmx-space-7);background:var(--aqmx-surface-sunken);flex:1 1 0;grid-template:minmax(0,1fr)/minmax(0,1fr);place-items:center;display:grid;overflow:auto}.aqmx-document-file[data-aqmx-media-type=pdf]>.aqmx-document-stage{place-items:stretch stretch;padding:0}.aqmx-document-video{border-radius:var(--aqmx-radius-l);background:var(--aqmx-surface-raised);max-block-size:100%;max-inline-size:100%;box-shadow:var(--aqmx-shadow)}.aqmx-document-audio{inline-size:min(100%, var(--aqmx-column-measure))}.aqmx-document-frame{background:var(--aqmx-surface);border:0;block-size:100%;inline-size:100%}.aqmx-document-inert-link{color:var(--aqmx-text-muted);cursor:not-allowed;text-decoration-style:dotted}.aqmx-document[data-aqmx-document-flow=content]{contain:layout;block-size:auto;overflow:visible}.aqmx-document[data-aqmx-document-flow=content]>*,.aqmx-document[data-aqmx-document-flow=content] .aqmx-document-file>*{flex:none}.aqmx-document[data-aqmx-document-flow=content] .aqmx-document-frame{aspect-ratio:1/1.41421;block-size:auto}.aqmx-document-refresh-bar{flex-direction:column;flex:none;display:flex}.aqmx-document-refresh-row{padding:calc(var(--aqmx-focus-ring-width) + var(--aqmx-focus-ring-offset));justify-content:flex-end;align-items:center;display:flex}.aqmx-document-refresh-failure{margin:0 var(--aqmx-space-3) var(--aqmx-space-2);padding:var(--aqmx-space-4) var(--aqmx-space-5);border:var(--aqmx-space-1) solid var(--aqmx-danger-border);border-radius:var(--aqmx-radius-m);color:var(--aqmx-danger-text);background:var(--aqmx-danger-surface);line-height:var(--aqmx-line-height-compact);overflow-wrap:anywhere}.aqmx-document-refresh-row>.aqmx-icon-button[data-aqmx-busy=true]>svg{animation:aqmx-explorer-spin calc(var(--aqmx-duration-4) * 3) linear infinite}.aqmx-document-refresh-row>.aqmx-icon-button[aria-busy=true]{cursor:progress}@media (prefers-reduced-motion:reduce){.aqmx-document-refresh-row>.aqmx-icon-button[data-aqmx-busy=true]>svg{animation:none}}.aqmx-multi-stage{--aqmx-multi-panel-body-floor:calc(3rem * var(--aqmx-line-height-prose));--aqdd-stage-padding-block:var(--aqmx-space-7);--aqdd-stage-padding-inline:var(--aqmx-space-6);--aqdd-column-gap:var(--aqmx-space-6);--aqdd-panel-surface:var(--aqmx-surface);--aqdd-panel-border:var(--aqmx-border);--aqdd-panel-title-color:var(--aqmx-text);--aqdd-header-button-color:var(--aqmx-text-muted);--aqdd-header-button-hover-color:var(--aqmx-text);--aqdd-header-button-hover-surface:var(--aqmx-surface-sunken);--aqdd-custom-badge-color:var(--aqmx-warning-text);--aqdd-custom-badge-surface:var(--aqmx-warning-surface);--aqdd-restore-button-color:var(--aqmx-surface);--aqdd-restore-button-surface:var(--aqmx-accent);--aqdd-restore-button-hover-surface:var(--aqmx-info-text);--aqdd-drop-placeholder-color:var(--aqmx-accent);--aqdd-drop-placeholder-surface:var(--aqmx-surface);--aqdd-drop-placeholder-target-surface:var(--aqmx-accent-surface);background:var(--aqmx-surface-sunken);block-size:100%;min-block-size:0;min-inline-size:0;color:var(--aqmx-text);flex-direction:column;display:flex;position:relative}.aqmx-multi-stage:focus-visible{outline:var(--aqmx-focus-ring-width) solid var(--aqmx-focus);outline-offset:calc(-1 * var(--aqmx-focus-ring-width))}.aqmx-multi-probe{inline-size:var(--aqmx-column-measure);visibility:hidden;pointer-events:none;block-size:0;font-size:1rem;position:absolute;inset-block-start:0;inset-inline-start:0;overflow:hidden}.aqmx-multi-layout{flex:auto;min-block-size:0}.aqmx-multi-skeleton{grid-template-columns:repeat(auto-fit, minmax(min(100%, var(--aqmx-column-measure)), 1fr));align-content:start;gap:var(--aqmx-space-7);padding:var(--aqmx-space-7);display:grid;overflow:hidden}.aqmx-multi-skeleton-column{border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-l);background:var(--aqmx-surface)}.aqmx-multi-column{min-inline-size:0}.aqmx-multi-panel{outline:var(--aqmx-space-1) solid var(--aqmx-border-strong);outline-offset:0;color:var(--aqmx-text)}.aqmx-multi-panel[data-maximized]{outline-color:var(--aqmx-focus)}.aqmx-multi-panel-frame{content-visibility:auto;contain-intrinsic-block-size:auto var(--aqmx-space-9);min-block-size:100%}.aqmx-multi-panel-frame:focus-visible{outline:var(--aqmx-focus-ring-width) solid var(--aqmx-focus);outline-offset:calc(-1 * var(--aqmx-focus-ring-width))}.aqmx-multi-panel-frame[data-aqmx-maximized=false]{-webkit-user-select:none;user-select:none}.aqmx-multi-panel-frame>.aqmx-document-refresh-bar{z-index:1;background:var(--aqmx-surface);position:sticky;inset-block-start:0}.aqmx-multi-panel-frame{--aqmx-multi-panel-pinned-block-size:0px}.aqmx-multi-panel-frame .aqmx-document *{scroll-margin-block-start:calc(var(--aqmx-multi-panel-pinned-block-size) + var(--aqmx-focus-ring-width) + var(--aqmx-focus-ring-offset))}.aqmx-multi-panel-frame .aqmx-document-toolbar>.aqmx-icon-button{flex-shrink:1;min-inline-size:0}.aqmx-multi-toast{animation:aqmx-multi-toast-in var(--aqmx-duration-3) var(--aqmx-ease)}.aqmx-multi-toast:popover-open{max-inline-size:min(34rem, calc(var(--aqmx-multi-toast-room) - var(--aqmx-space-8)));margin:0;position:fixed;inset:auto;inset-block-start:calc(var(--aqmx-multi-toast-block-start) + var(--aqmx-space-6));inset-inline-end:calc(var(--aqmx-multi-toast-inline-end) + var(--aqmx-space-7));overflow:visible}@keyframes aqmx-multi-toast-in{0%{opacity:0;translate:0 calc(var(--aqmx-space-5) * -1)}}@media (prefers-reduced-motion:reduce){.aqmx-multi-toast{animation:none}}@media (forced-colors:active){.aqmx-multi-stage:focus-visible,.aqmx-multi-stage :focus-visible,.aqmx-multi-panel[data-maximized]{outline-color:highlight}}.aqmx-split-frame{--aqmx-sheet-target:2.75rem;flex-direction:column;flex:auto;min-block-size:0;min-inline-size:0;display:flex;position:relative}.aqmx-rem-probe{visibility:hidden;pointer-events:none;block-size:0;inline-size:1rem;position:absolute;inset-block-start:0;inset-inline-start:0}.aqmx-split{flex:auto;min-block-size:0}.aqmx-split-explorer,.aqmx-split-content{background:var(--aqmx-surface);min-block-size:0}.aqmx-split-explorer{background:var(--aqmx-surface-raised)}.aqmx-split-handle{background:var(--aqmx-surface-raised);transition:background-color var(--aqmx-duration-2) var(--aqmx-ease)}.aqmx-split-handle:hover,.aqmx-split-handle:focus-visible{background:var(--aqmx-surface-sunken)}.aqmx-sheet-bar{padding:var(--aqmx-space-3) var(--aqmx-space-5);border-block-end:var(--aqmx-space-1) solid var(--aqmx-border);background:var(--aqmx-surface-raised);flex:none;align-items:center;display:flex}.aqmx-sheet-toggle,.aqmx-sheet-close{justify-content:center;align-items:center;gap:var(--aqmx-space-5);min-inline-size:var(--aqmx-sheet-target);min-block-size:var(--aqmx-sheet-target);padding:0 var(--aqmx-space-6);border:var(--aqmx-space-1) solid var(--aqmx-border);border-radius:var(--aqmx-radius-m);color:var(--aqmx-text);background:var(--aqmx-surface);font:inherit;cursor:pointer;transition:background-color var(--aqmx-duration-2) var(--aqmx-ease);display:inline-flex}.aqmx-sheet-close{z-index:1;color:var(--aqmx-text-muted);background:var(--aqmx-surface-raised);border-color:#0000;padding:0;position:relative}.aqmx-sheet-toggle:hover,.aqmx-sheet-close:hover{background:var(--aqmx-surface-sunken)}.aqmx-sheet-toggle>svg,.aqmx-sheet-close>svg{inline-size:var(--aqmx-icon);block-size:var(--aqmx-icon);flex:none}.aqmx-sheet{inline-size:min(calc(100vw - var(--aqmx-space-9)), 21rem);border:0;border-inline-end:var(--aqmx-space-1) solid var(--aqmx-border);block-size:100dvh;max-block-size:none;max-inline-size:none;color:var(--aqmx-text);background:var(--aqmx-surface-raised);box-shadow:var(--aqmx-shadow);margin:0;padding:0;position:fixed;inset-block:0;inset-inline:0 auto}.aqmx-sheet::backdrop{background:#0f172a61}.aqmx-sheet-panel{flex-direction:column;block-size:100%;display:flex}.aqmx-sheet-header{z-index:1;justify-content:flex-end;align-items:center;gap:var(--aqmx-space-3);padding:var(--aqmx-space-3);border-block-end:var(--aqmx-space-1) solid var(--aqmx-border);flex:none;display:flex;position:relative}.aqmx-sheet-notice-slot>.aqmx-toast{--aqmx-sheet-notice-band:calc(var(--aqmx-sheet-target) + var(--aqmx-space-3) * 2);--aqmx-sheet-notice-line:calc(var(--aqmx-line-height-compact) * 1em);--aqmx-sheet-notice-inset:calc((var(--aqmx-sheet-notice-band) - var(--aqmx-sheet-notice-line)) / 2);z-index:0;max-inline-size:none;padding-block:var(--aqmx-sheet-notice-inset) 0;padding-inline:var(--aqmx-space-6) var(--aqmx-space-3);border:0;border-block-end:var(--aqmx-space-1) solid var(--aqmx-warning-border);line-height:var(--aqmx-line-height-compact);overflow-wrap:anywhere;box-shadow:none;border-radius:0;position:absolute;inset-block:0 auto;inset-inline:0}.aqmx-sheet-notice-slot>.aqmx-toast:before{content:"";float:inline-end;inline-size:calc(var(--aqmx-sheet-target) + var(--aqmx-space-3));block-size:calc(var(--aqmx-sheet-notice-band) - var(--aqmx-sheet-notice-inset) - var(--aqmx-space-1))}.aqmx-sheet-notice-slot>.aqmx-toast:after{content:"";block-size:calc(var(--aqmx-sheet-notice-inset) - var(--aqmx-space-1));display:block}.aqmx-sheet-body{isolation:isolate;flex-direction:column;flex:auto;min-block-size:0;display:flex;position:relative;overflow:hidden}.aqmx-document-column{flex-direction:column;min-block-size:0;display:flex}.aqmx-document-column>.aqmx-document{flex:1 1 0;block-size:auto;min-block-size:0}.aqmx-single{flex:auto;position:relative}.aqmx-tree-document{block-size:100%;inline-size:100%}.aqmx-split-fallback,.aqmx-stage-fallback{background:var(--aqmx-surface-sunken);flex:auto;min-block-size:0}@media (forced-colors:active){.aqmx-sheet-toggle[aria-expanded=true]{forced-color-adjust:none;color:highlighttext;background:highlight}.aqmx-sheet-toggle:focus-visible,.aqmx-sheet-close:focus-visible{outline-color:highlight}.aqmx-split-handle:focus-visible{outline:var(--aqmx-focus-ring-width) solid Highlight!important;outline-offset:calc(-1 * var(--aqmx-focus-ring-width))!important}}@media (prefers-reduced-motion:reduce){.aqmx-split-handle,.aqmx-split-handle *{transition-duration:0s!important}}}@layer utilities;
@@ -0,0 +1,95 @@
1
+ # DOM hooks
2
+
3
+ This page is part of the host-facing contract of `@aiquants/markdown-explorer`; its summary is in the [README](../../README.md#dom-hooks).
4
+
5
+ Tests and host styling may rely on the `data-testid` values and `data-aqmx-*` attributes below; they keep their meaning across releases, and a change to them is a breaking change. Class names (`aqmx-*`) are styling details and change freely — never select on them. Hooks rendered by the peer packages (`@aiquants/directory-tree`, `@aiquants/drag-drop-panels`, `@aiquants/markdown`) belong to those packages.
6
+
7
+ ## Frame
8
+
9
+ | Hook | Element | Meaning |
10
+ | --- | --- | --- |
11
+ | `data-testid="markdown-explorer"` | Root | Carries `data-aqmx-theme` (`light` / `dark`) and `data-aqmx-view` (`tree` / `multi` / `single`) |
12
+ | `data-testid="markdown-explorer-announcer"` | The root's polite live region (`role="status"`) | While it holds the announcements (no sheet is open): the text of the last announcement frame — its one message, or every message posted within that frame in order (the items of one list label merged into one sentence in the place of the first), joined by `announcementSequence`; empty while the next frame is pending, in a new reader's explorer, and while the sheet holds the announcements |
13
+ | `data-aqmx-explorer-mode` | Frame of the tree and multi views | `split` (the explorer beside the content) or `sheet` (the frame is narrower than 13rem × φ², and the explorer opens as a sheet) |
14
+ | `data-aqmx-split` | Explorer/content split | The view it splits (`tree` / `multi`) |
15
+ | `data-testid="markdown-explorer-sheet-toggle"` | Button that opens the explorer sheet | Carries `aria-expanded`. Also receives the focus after a view switch from the header's link when the frame is a sheet |
16
+ | `data-testid="markdown-explorer-sheet"` | The explorer sheet (a modal `dialog`) | Has the `open` attribute while shown |
17
+ | `data-testid="markdown-explorer-sheet-close"` | Close button of the sheet | |
18
+ | `data-testid="markdown-explorer-sheet-announcer"` | The sheet's polite live region (`role="status"`), inside the dialog | Holds the announcements while the sheet is open (the modal dialog hides the root's region), with the same text as the root's region would; empty while the sheet is closed and when it opens |
19
+ | `data-testid="markdown-explorer-split-loading"` | Placeholder while the tree view's code loads | |
20
+ | `data-testid="markdown-explorer-single"` | Single view frame | |
21
+ | `data-testid="markdown-explorer-document-refresh-bar"` | The document refresh bar | In the tree and single views, above the document region (the button at its inline end; absent while the tree view shows no document); in the multi view, the first row of each panel's body, inside the panel's scroll area and pinned (sticky) to its top while the document scrolls under it. Holds the button row and, below it, the failure notice |
22
+ | `data-testid="markdown-explorer-document-refresh"` | The Refresh document button, in the bar | Reads that document alone again past the host's caches. The page views name it by its visible label (`refreshDocumentLabel`; tooltip `refreshDocument`); a panel's is icon-only, with `aria-label` and `title` set to `refreshDocumentNamed` (the panel title). Carries `aria-busy="true"` and `data-aqmx-busy="true"` while it runs, and `aria-disabled="true"` then and while the document loads (never `disabled`) |
23
+ | `data-testid="markdown-explorer-document-refresh-failure"` | Notice of a document refresh that failed, inside the bar below its button row | No live role (the announcer speaks the same text); carries `data-aqmx-failure` (the reason). Shown while the copy of the document it kept stays on screen: a newer answer that replaces or confirms that copy, a move to another document (it does not come back on return) and that control's next refresh take it down |
24
+ | `data-aqmx-key-repeat="allow"` | A control inside the explorer, or an element around controls | Opts the control (or every control inside the element) in to the key repeat of a held Enter, which the explorer otherwise ignores: a press of Enter that began on the control keeps activating it once per repeat while the focus stays there, and a press that began elsewhere and handed the focus to it does not (see [Held keys](layout.md#held-keys)); the explorer sets it on Zoom in and Zoom out |
25
+
26
+ ## Explorer
27
+
28
+ | Hook | Element | Meaning |
29
+ | --- | --- | --- |
30
+ | `data-testid="markdown-explorer-explorer"` | The explorer `nav` | Carries `data-aqmx-mode` (`tree` / `multi`) |
31
+ | `data-testid="markdown-explorer-source-select"` | Source switcher | Shown with more than one source |
32
+ | `data-testid="markdown-explorer-refresh"` | The explorer's Refresh tree button | Named by its visible label (`refreshTreeLabel`; tooltip `refreshTree`). Carries `aria-disabled="true"`, `aria-busy="true"` and `data-aqmx-busy="true"` while the tree reloads (never `disabled`) |
33
+ | `data-testid="markdown-explorer-refresh-failure"` | Alert of a tree reload that failed | `role="alert"`, first in the explorer's body; carries `data-aqmx-failure` (the reason). The old tree stays below it; shown until the next refresh starts, and only while its source is the pane's |
34
+ | `data-testid="markdown-explorer-expand-all"` / `-collapse-all` | Tree controls | `aria-disabled="true"` while unavailable (never `disabled`). Expand all also carries `aria-busy="true"`, `data-aqmx-busy="true"` and `aria-disabled="true"` while a press runs (it ignores presses meanwhile); Collapse all ends a running press |
35
+ | `data-testid="markdown-explorer-tree-settings"` | Tree settings button | |
36
+ | `data-testid="markdown-explorer-tree-settings-menu"` | Tree settings menu | |
37
+ | `data-testid="markdown-explorer-tree-settings-item"` | One setting | Carries `data-aqmx-setting` (the setting's key, such as `sortMode`) |
38
+ | `data-testid="markdown-explorer-view-switch"` | Link to the other view | Carries `data-aqmx-target` (`tree` / `multi`) |
39
+ | `data-testid="markdown-explorer-notice"` | One notice | Carries `data-aqmx-notice` (its id) and `data-aqmx-tone` (`info` / `warning` / `danger`) |
40
+ | `data-testid="markdown-explorer-expand-all-stop"` | Notice of an Expand all press that stopped at a limit, in the explorer's body just above the tree area (not a live region, not focusable) | Holds the stop sentence announced at the stop (`expandAllFolderLimit` / `expandAllDepthLimit`); shown until the next press, Collapse all, a pane with no rows (failed, empty or cleared) or unmount (a source or view switch). Carries `data-aqmx-stop` (`folder-limit` / `depth-limit`) |
41
+ | `data-testid="markdown-explorer-tree"` | Tree area | |
42
+ | `data-testid="markdown-explorer-tree-scroll"` | The tree's scroll pane (the virtual list's root) | |
43
+ | `data-testid="markdown-explorer-tree-loading"` | Tree placeholder while it loads | |
44
+ | `data-testid="markdown-explorer-tree-empty"` | Empty tree message | |
45
+ | `data-testid="markdown-explorer-tree-progress"` | Progress line floating at the bottom of the tree area (not a live region) | Shown while an Expand all press runs, and otherwise while the source has folder listings queued or in flight (after a press stopped at a limit too: the stop sentence is `markdown-explorer-expand-all-stop`'s, never this line's). Carries `data-aqmx-loading` (the source's queued and in-flight listings, whichever rows are drawn) and `data-aqmx-expand-all` (`idle` / `running` / `stopped`) |
46
+ | `data-testid="markdown-explorer-tree-failure"` | Tree failure | Carries `data-aqmx-failure` (the reason) and holds the failure's callout, `role="alert"`; a host's `renderTreeFailure` view replaces the callout inside it (this element and its hooks stay) and must announce itself |
47
+ | Tree rows | `role="treeitem"` | Carry `data-aqmx-path` (the entry's path; the split also returns the focus to the same row through it after the sheet threshold), `data-aqmx-open` on documents open as panels (open rows are styled through it), and `data-aqmx-children` (`loading` / `failed`) on lazy folders: `loading` only while some tree still shows the folder open (a cancelled folder loses it). The shown document's row carries `aria-current="page"` in the tree view with Selection Single or Multiple |
48
+ | Loop rows | `role="treeitem"` with `data-aqmx-loop` | A folder met again among its own ancestors (a shortcut inside its own target): `data-aqmx-loop` is the path of the folder it loops back to, and the row has no `data-aqmx-path`. It never expands; activating it scrolls to that folder, above it, and moves the focus there |
49
+ | `data-testid="markdown-explorer-folder-retry"` | Retry inside a folder whose children failed | Named by its text (`retry`) also while a narrow row shows only its icon, and described by the failure text (`aria-describedby`); activating it while it holds the focus moves the focus to the folder's row before the children load again |
50
+ | `data-testid="markdown-explorer-selection-tray"` | Selection tray (multiple selection) | |
51
+ | `data-testid="markdown-explorer-selection-remove"` | Remove one selected document | Carries `data-aqmx-path` |
52
+ | `data-testid="markdown-explorer-selection-open"` / `-selection-clear` | Open the selection as panels, clear it | |
53
+
54
+ The tree is named by `treeRegion`, apart from the explorer `nav` (`explorerRegion`). Rows expose the selection with `aria-selected`, and the tree carries `aria-multiselectable` in the tree view's Multiple mode and in the multi-panel view: in the tree view, Single selects the shown document, Multiple the documents gathered in the tray, Off none; in the multi-panel view, the documents open as panels. The tree's stand-in Tab stop, present while the tab-stop row is outside the tree's render window, is `@aiquants/directory-tree`'s `data-directory-tree-tab-stop` (see [The tree](layout.md#the-tree)).
55
+
56
+ ## Document
57
+
58
+ | Hook | Element | Meaning |
59
+ | --- | --- | --- |
60
+ | `data-testid="markdown-explorer-document"` | Document region (`role="region"`) | Carries `data-aqmx-document-kind`: `empty`, `loading`, `markdown`, `text`, `image`, `media`, `file` or `failure`. Named by `documentRegion` in the tree and single views, and in a multi-view panel by the document's name (the last segment of its path before it loads and for a failure). Not focusable: after a click in a document's text, PageDown, the arrow keys, Space, Home and End scroll the document |
61
+ | `data-testid="markdown-explorer-document-empty"` | No document selected | |
62
+ | `data-testid="markdown-explorer-document-skeleton"` | Placeholder while a pane has no document to show yet (its first document, or one the server gave up on) | The region carries `aria-busy="true"` |
63
+ | `data-testid="markdown-explorer-document-progress"` | Progress bar while the next document (or a fresh copy of a failed one) loads over the one still shown | The region carries `aria-busy="true"` meanwhile, also during router navigations |
64
+ | `data-testid="markdown-explorer-document-failure"` | Document failure | Carries `data-aqmx-failure` (the reason); its callout has no role in every view (tree, single and multi-view panels): the announcer speaks the failure (`documentFailed`), once for each failed attempt |
65
+ | `data-testid="markdown-explorer-text-truncated"` | Notice under a cut text preview | |
66
+ | `data-testid="markdown-explorer-image-view"` | Image viewer | |
67
+ | `data-testid="markdown-explorer-image-canvas"` | The image's scroll area | |
68
+ | `data-testid="markdown-explorer-image"` | The image (`img`) | Carries `data-aqmx-image-mode` (`fit` / `level`) |
69
+ | `data-testid="markdown-explorer-image-level"` | The zoom level (an `output`) | A polite status: assistive technology reads the new level when it changes |
70
+ | `data-testid="markdown-explorer-media-view"` / `-media-player` | Media viewer and its player | The view carries `data-aqmx-media-type` (`video` / `audio` / `pdf`) |
71
+ | `data-testid="markdown-explorer-file-name"` / `-file-open` / `-file-download` | Header of an image, media, text or file view | |
72
+ | `data-testid="markdown-explorer-file-view"` | The view of a file the explorer cannot show (a `file` document) | Holds the file header and the note below. |
73
+ | `data-testid="markdown-explorer-file-not-shown"` / `-file-not-shown-download` | The file view's note (`role="note"`, the label `fileNotShown`) and its download link | The link's `href` is the document's `url` and its `download` the file's name. |
74
+
75
+ Links inside Markdown to documents carry `data-link-kind="doc"` and their `data-doc-path`, and `data-doc-exists` only as `"false"` (a `"true"` the server sent is not rendered). An inert link — one the server sent without `data-doc-path`, or whose `data-doc-path` `isAcceptableDocumentPath` refuses — carries `data-link-kind="doc"` and `data-doc-exists="false"` and no `data-doc-path`.
76
+ Every link the explorer renders inside Markdown also keeps the document's own `data-footnote-ref` or `data-footnote-backref` (a footnote reference and its back-reference), beside its `title`, `lang`, `dir`, `role`, `aria-*` and an `id` in the `user-content-` namespace (an inert link's `title` is the explorer's reason). A document's own classes and ids are no hooks: the viewer keeps only its own class tokens and the ids of headings, footnotes and the `user-content-` namespace.
77
+
78
+ ## Multi-panel view
79
+
80
+ | Hook | Element | Meaning |
81
+ | --- | --- | --- |
82
+ | `data-testid="markdown-explorer-stage"` | The stage | Carries `data-aqmx-visible-columns` (the number of visible columns; the logical column count until the stage is first measured). Focusable (`tabIndex=-1`): it receives the focus when the panel holding the focus is closed or hidden and no other panel is shown |
83
+ | `data-testid="markdown-explorer-stage-loading"` | Placeholder while the view's code loads | |
84
+ | `data-testid="markdown-explorer-stage-skeleton"` | Placeholder before the layout is measured (also the server render) | |
85
+ | `data-testid="markdown-explorer-multi-empty"` | No panels open | |
86
+ | `data-testid="markdown-explorer-panel-frame"` | One panel's content | `role="group"` named after the panel's document (the panel title); carries `data-aqmx-panel-frame` (the panel id) and `data-aqmx-maximized` (`true` / `false`). A host can tell that a panel is maximized (and covers its chrome) with `:has([data-aqmx-panel-frame][data-aqmx-maximized="true"])` |
87
+ | `data-testid="markdown-explorer-panel-limit"` | Visible notice that the panel limit was reached (not a live region; the announcer speaks it) | Inside the open sheet's header while the sheet is open, spanning the header from its top edge with its whole text wrapped around the close button, which stays above it: one line covers exactly the header's band, and more lines spill over the top of the sheet's body (the header never changes size); otherwise a manual popover at the stage's corner, or, in browsers without the Popover API, an element without `popover` drawn in place there. Removed after three seconds, or as soon as the focus moves onto an element it is painted over (a control painted above it, such as the sheet's close button, keeps it) |
88
+
89
+ A panel id is `aqmx-p-` followed by a hash of the document path that first opened the panel; it stays with the panel when the reader follows a link inside it.
90
+
91
+ Everything around a panel's content is drawn by `@aiquants/drag-drop-panels` and selected through its own hooks: the stage's scroll root (`data-aqdd-scroll-root`, a `div`), each column's region (`data-column-key`), a panel (`data-panel-id`, the panel id above, with `data-maximized` while maximized), its header's title, badge and buttons (`data-aqdd-panel-title`, `data-aqdd-custom-badge`, and `data-aqdd-panel-action` naming each button: `move-previous-column`, `move-next-column`, `move-up`, `move-down`, `drag-mode`, `hide`, `maximize`, `restore`, `close`), the panel a drag lifted (`data-dragging="true"`), the drop placeholders of a drag (`data-aqdd-drop-placeholder`: `hidden`, `shown`, or `target` for the one a drop would land on; with `data-placeholder-column` and `data-placeholder-index`), the bar of hidden panels (`data-aqdd-hidden-panels`) and its restore buttons (`data-aqdd-restore-panel`), and the live region that speaks a move and a touch drag's lift and cancel (`data-aqdd-announcer`).
92
+
93
+ ## Failures and tones
94
+
95
+ `data-aqmx-failure` takes the failure reasons: `unauthorized`, `forbidden`, `not-found`, `not-configured`, `invalid`, `too-large`, `unsupported` and `failed`. `data-aqmx-tone` on a callout is `danger` for `unauthorized`, `forbidden` and `failed`, and `warning` for the other reasons. The explorer's own failure callout offers "Try again" (`retry`) only for `failed`, a reload of the page (`reloadPage`) for `unauthorized`, and no button for the other reasons, which retrying cannot change.