gromajs 0.0.0-stage → 0.3.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gabriel Franco
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,124 @@
1
- # Temporary Holding Version
1
+ <img src="docs/images/groma-logo.svg" width="88" height="88" alt="Groma logo: a surveyor's groma with two plumb lines and a red measuring line" />
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ # Groma
4
+
5
+ Survey any design before you build it.
6
+
7
+ Groma is a Figma-style inspector for design handoff pages. Add one script to
8
+ an HTML design file and inspect it the way you would inspect a frame in
9
+ Figma's Dev Mode: box model, specs, design tokens, components and distances.
10
+
11
+ It has no runtime dependencies and ships as a single file.
12
+
13
+ > A *groma* was the Roman surveyor's instrument: a staff with a cross and
14
+ > plumb lines, used to lay out straight lines and right angles before roads
15
+ > and cities were built. This one does the same job on a design.
16
+
17
+ ## Why this exists
18
+
19
+ More and more designs are now produced by AI tools, and they arrive as
20
+ working HTML, not as Figma files. That is good for speed: a design can go
21
+ from an idea to something you can click in minutes, and be revised just as
22
+ fast. But it leaves the engineer who has to build it without the tools a
23
+ design file used to give them.
24
+
25
+ - **Browser devtools answer the wrong question.** They show computed CSS,
26
+ hundreds of properties per element. They do not say which design token a
27
+ colour came from, which component an element is, whether a width is fixed
28
+ or fills its parent, or how far two elements are from each other.
29
+ - **The handoff is the HTML itself.** There is no separate spec to read, so
30
+ the inspection has to happen on the page.
31
+ - **Designs change often.** When a new version takes minutes to produce,
32
+ you need to see what changed between two exports without comparing them
33
+ by eye.
34
+
35
+ Groma puts those answers on the page. It reads the design the way
36
+ an engineer needs it: the value to type, the token to use, the component to
37
+ import, the distance to the next element.
38
+
39
+ ## What it does
40
+
41
+ | Keys | What happens |
42
+ | --- | --- |
43
+ | Alt + hover | The hovered element's box model and a chip with its name, size and position |
44
+ | Alt + click | Pins the element and opens the side panel |
45
+ | Alt + hover, with an element pinned | Distances from the pinned element, in red |
46
+ | Alt + right-click | A menu of every layer under the pointer |
47
+
48
+ A pill at the bottom right locks inspect mode on, so none of this needs Alt.
49
+
50
+ The side panel answers four questions:
51
+
52
+ - **Specs**: what do I need to build this? Size and sizing intent, a box
53
+ diagram, colours with their design tokens, typography, motion, and the
54
+ component the element belongs to. A click copies any value.
55
+ - **All CSS**: what is every computed property? Values are editable, to try
56
+ a change on the spot.
57
+ - **Layers**: where does this sit in the tree?
58
+ - **Changes**: what changed since the last export? (Claude Design exports.)
59
+
60
+ ## Try it
61
+
62
+ You need [Node.js](https://nodejs.org) 22.12 or newer and Google Chrome.
63
+
64
+ ```bash
65
+ git clone https://github.com/franco-dias/groma.git
66
+ ```
67
+
68
+ ```bash
69
+ cd groma
70
+ ```
71
+
72
+ ```bash
73
+ npm install
74
+ ```
75
+
76
+ Open any HTML design file with Groma attached. The file is not edited:
77
+
78
+ ```bash
79
+ npm run inspect -- path/to/design.html
80
+ ```
81
+
82
+ Or see it on the built-in demo page:
83
+
84
+ ```bash
85
+ npm run serve
86
+ ```
87
+
88
+ Then hold Alt and move the pointer over the page.
89
+
90
+ ## Add it to a design file
91
+
92
+ Build the script once:
93
+
94
+ ```bash
95
+ npm run build
96
+ ```
97
+
98
+ Copy `dist/groma.js` next to your HTML file and add it as the first tag
99
+ inside `<head>`:
100
+
101
+ ```html
102
+ <script src="groma.js"></script>
103
+ ```
104
+
105
+ It works on a file opened from disk and on a served page, in Chrome and
106
+ other Chromium browsers. Groma is on npm, so you can also load it from a CDN with no copy step:
107
+
108
+ ```html
109
+ <script src="https://cdn.jsdelivr.net/npm/gromajs@0.3.0/dist/groma.js"></script>
110
+ ```
111
+
112
+ ## Learn more
113
+
114
+ | Read | For |
115
+ | --- | --- |
116
+ | [docs/usage.md](docs/usage.md) | Command options, settings, how components are found, the API for host pages, known limits |
117
+ | [docs/features.md](docs/features.md) | Every feature and the rules it follows |
118
+ | [docs/development.md](docs/development.md) | Scripts, tests, how the code is organised, releasing |
119
+ | [AGENTS.md](AGENTS.md) | The rules for changing the code, for AI agents and people |
120
+ | [CHANGELOG.md](CHANGELOG.md) | What changed in each version |
121
+
122
+ ## Licence
123
+
124
+ [MIT](LICENSE).
package/dist/groma.js ADDED
@@ -0,0 +1,197 @@
1
+ (function(){var e=null,t=[],n=()=>e;function r(e){t.push(e)}function i(n){t.forEach(t=>t(e,n))}function a(t){e=t,i(`pinned`)}function o(){e=null,i(`pinned`)}function s(){i(`updated`)}var c={copyFormat:`css`,tokenPrefixes:[]},l={...c};function u(e=c){(e.copyFormat===`css`||e.copyFormat===`sx`)&&(l.copyFormat=e.copyFormat),Array.isArray(e.tokenPrefixes)&&(l.tokenPrefixes=e.tokenPrefixes.filter(e=>e.startsWith(`--`))),s()}function d(){let e=document.currentScript??document.querySelector(`script[data-groma]`);e instanceof HTMLElement&&u({copyFormat:e.dataset.copy,tokenPrefixes:e.dataset.tokenPrefix?.split(/[\s,]+/).filter(Boolean)})}function f(e){try{return JSON.parse(localStorage.getItem(e)??`{}`)??{}}catch{return{}}}function p(e,t){try{localStorage.setItem(e,JSON.stringify(t))}catch{}}var m=`__groma_overrides:`+location.pathname,h=`__groma_html:`+location.pathname,ee=new WeakMap,te=new WeakMap,ne=e=>e.style;function re(e){return f(m)[e]??{}}function ie(e,t,n){let r=f(m),i=r[e]??={};n===null?delete i[t]:i[t]=n,Object.keys(i).length||delete r[e],p(m,r)}function ae(e,t,n){let r=ne(e),i=ee.get(e)??{};ee.set(e,i),i[t]??=[r.getPropertyValue(t),r.getPropertyPriority(t)],r.setProperty(t,n)}function oe(e,t){let[n,r]=ee.get(e)?.[t]??[``,``];ne(e).setProperty(t,n,r)}var se=new Set([`input`,`textarea`,`select`,`option`]),g=e=>se.has(e.localName);function ce(e){return g(e)?e.value:e.innerHTML}function le(e,t){g(e)?e.value=t:e.innerHTML=t}function ue(e){return e in f(h)}function de(e,t){let n=f(h);t===null?delete n[e]:n[e]=t,p(h,n)}function fe(e,t){te.has(e)||te.set(e,ce(e)),le(e,t)}function pe(e){let t=te.get(e);t!==void 0&&le(e,t)}function me(e,t,n){try{e.querySelectorAll(t).forEach(n)}catch{}}function _(e=document){Object.entries(f(m)).forEach(([t,n={}])=>{me(e,t,e=>{Object.entries(n).forEach(([t,n])=>ae(e,t,n))})}),Object.entries(f(h)).forEach(([t,n=``])=>{me(e,t,e=>fe(e,n))})}function v(e,t=``,n){let r=document.createElement(e);return t&&(r.className=t),n!==void 0&&(r.textContent=n),r}function y(e,t){e.style.left=`${t.left}px`,e.style.top=`${t.top}px`,e.style.width=`${t.width}px`,e.style.height=`${t.height}px`}function b(e,t){let{offsetWidth:n,offsetHeight:r}=e;return{x:n?t.width/n:1,y:r?t.height/r:1}}function he(e,t){let n=t.left,r=t.top,i=t.left+t.width,a=t.top+t.height;for(let t=e.parentElement;t;t=t.parentElement){let{overflowX:e,overflowY:o}=getComputedStyle(t);if(e===`visible`&&o===`visible`)continue;let s=t.getBoundingClientRect();n=Math.max(n,s.left),r=Math.max(r,s.top),i=Math.min(i,s.right),a=Math.min(a,s.bottom)}return{left:n,top:r,width:Math.max(0,i-n),height:Math.max(0,a-r)}}function ge(e,t){let{offsetWidth:n,offsetHeight:r}=e,i=e=>Math.round(e*100)/100;return{width:n??i(t.width),height:r??i(t.height)}}var _e=`http://www.w3.org/2000/svg`,ve={check:`<path d="M20 6 9 17l-5-5"/>`,arrow:`<path d="M5 12h14M13 6l6 6-6 6"/>`,close:`<path d="M18 6 6 18M6 6l12 12"/>`,chevronDown:`<path d="m6 9 6 6 6-6"/>`,chevronRight:`<path d="m9 18 6-6-6-6"/>`,chevronUp:`<path d="m18 15-6-6-6 6"/>`,copy:`<rect x="9" y="9" width="12" height="12" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/>`,file:`<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>`,warning:`<path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/><path d="M12 9v4M12 17h.01"/>`,inspect:`<circle cx="12" cy="12" r="8"/><path d="M12 2v4M12 18v4M2 12h4M18 12h4"/>`},ye={fixed:`<path d="M2 6h8M2 3.5v5M10 3.5v5"/>`,hug:`<path d="M1.5 3.5 4 6 1.5 8.5M10.5 3.5 8 6l2.5 2.5M6 3v6"/>`,fill:`<path d="M1.5 6h9M4 3.5 1.5 6 4 8.5M8 3.5 10.5 6 8 8.5"/>`};function be(e,t,n){let r=document.createElementNS(_e,`svg`);return r.setAttribute(`class`,`icon`),r.setAttribute(`width`,String(t)),r.setAttribute(`height`,String(t)),r.setAttribute(`viewBox`,`0 0 ${n} ${n}`),r.innerHTML=e,r}var x=(e,t=14)=>be(ve[e],t,24),xe=e=>be(ye[e],12,12);function Se([e,t,n,r]){let i=e=>2+12*e,a=e=>14-12*e;return be(`<path d="M2 2v12h12" opacity="0.4"/><path d="${`M2 14C${i(e)} ${a(t)} ${i(n)} ${a(r)} 14 2`}"/>`,16,16)}var Ce=`:host{all:initial;--ins-font-sans:system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;--ins-font-mono:ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;--ins-bg:#171717;--ins-bg-raised:#1f1f1f;--ins-bg-hover:#262626;--ins-bg-active:#303030;--ins-border:#2a2a2a;--ins-border-strong:#3d3d3d;--ins-fg:#fafafa;--ins-fg-muted:#a3a3a3;--ins-fg-subtle:#8f8f8f;--ins-token:#8ab4ff;--ins-pin:#2f6bff;--ins-pin-tag:#2563eb;--ins-pin-fg:#fff;--ins-pin-tint:#2f6bff33;--ins-hover-line:#3b82f6;--ins-measure:#d92d20;--ins-measure-fg:#fff;--ins-warn:#f5b84a;--ins-warn-bg:#f5b84a1f;--ins-ok:#4ade80;--ins-ok-bg:#4ade8029;--ins-added:#4ade80;--ins-added-bg:#4ade801f;--ins-removed:#ff7b72;--ins-removed-bg:#ff7b721f;--ins-removed-line:#ff7b7273;--ins-box-margin-bg:#f6b26b1a;--ins-box-margin-line:#8a6a45;--ins-box-padding-bg:#86c7781a;--ins-box-padding-line:#557a4d;--ins-box-content-bg:#6fa8dc29;--ins-box-content-line:#4c6f93;--ins-swatch-ring:#ffffff38;--ins-checker:repeating-conic-gradient(#4a4a4a 0 25%, #2a2a2a 0 50%) 0 0 / 6px 6px;--ins-shadow:0 12px 32px #00000047, 0 2px 6px #0003;--ins-shadow-sm:0 4px 12px #00000040;--ins-s-1:2px;--ins-s-2:4px;--ins-s-3:6px;--ins-s-4:8px;--ins-s-5:12px;--ins-s-6:16px;--ins-s-7:28px;--ins-row:24px;--ins-row-sm:22px;--ins-label-w:56px;--ins-indent:12px;--ins-bar-h:40px;--ins-menu-w:260px;--ins-panel-w:380px;--ins-radius-xs:3px;--ins-radius-sm:4px;--ins-radius-md:6px;--ins-radius:8px;--ins-radius-pill:9999px;--ins-dur:.1s;--ins-dock:0px;z-index:2147483647;color:var(--ins-fg);font:400 12px/16px var(--ins-font-sans);position:fixed;top:0;left:0}*{box-sizing:border-box}button{all:unset;box-sizing:border-box;cursor:pointer}[hidden]{display:none!important}.icon{fill:none;stroke:currentColor;stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round;flex:none}.spacer{flex:1;min-width:0}kbd{min-width:18px;height:18px;padding:0 var(--ins-s-1);color:var(--ins-fg);background:var(--ins-bg-raised);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-xs);font:400 11px/16px var(--ins-font-mono);place-items:center;display:inline-grid}.swatch{border-radius:var(--ins-radius-xs);background:linear-gradient(var(--swatch), var(--swatch)), var(--ins-checker);width:12px;height:12px;box-shadow:inset 0 0 0 1px var(--ins-swatch-ring);flex:none}.swatch.is-small{border-radius:2px;width:8px;height:8px}.copy{cursor:pointer}.copy.is-copied{background:var(--ins-ok-bg);box-shadow:0 0 0 2px var(--ins-ok-bg);border-radius:2px}.copy.is-copy-blocked{background:var(--ins-removed-bg);box-shadow:0 0 0 2px var(--ins-removed-bg);border-radius:2px}.overlay,.overlay>div,.outline,.tooltip,.grid{pointer-events:none;position:fixed}.overlay{z-index:3;width:0;height:0;top:0;left:0}.margin{background:#f6b26b73}.border{background:#e0666673}.padding{background:#93c47d73}.content-box{background:#6fa8dc66}.gap{background:#ff64824d}.child{background:#b482dc40;border:1px solid #a064d2b3}.target{border:1px dashed var(--ins-hover-line)}.measure{background:var(--ins-measure)}.measure:before,.measure:after{content:"";background:var(--ins-measure);position:absolute}.measure.is-x:before,.measure.is-x:after{width:1px;height:7px;top:-3px}.measure.is-x:before{left:0}.measure.is-x:after{right:0}.measure.is-y:before,.measure.is-y:after{width:7px;height:1px;left:-3px}.measure.is-y:before{top:0}.measure.is-y:after{bottom:0}.overlay>.measure-label{height:16px;padding:0 var(--ins-s-2);border-radius:var(--ins-radius-xs);color:var(--ins-measure-fg);background:var(--ins-measure);font:500 11px/16px var(--ins-font-mono);white-space:nowrap}.outline{z-index:2;display:none}.outline-pinned{border:2px solid var(--ins-pin)}.outline-hover{border:1px dashed var(--ins-hover-line)}.pin-tag{align-items:center;gap:var(--ins-s-3);height:18px;padding:0 var(--ins-s-3);color:var(--ins-pin-fg);background:var(--ins-pin-tag);border-radius:var(--ins-radius-sm) var(--ins-radius-sm) 0 0;white-space:nowrap;font:400 11px/16px var(--ins-font-mono);display:flex;position:absolute;bottom:100%;left:-2px}.pin-tag b{font:600 11px/16px var(--ins-font-sans)}.pin-tag.is-inside{border-radius:0 0 var(--ins-radius-sm) 0;top:0;bottom:auto;left:0}.tooltip{z-index:4;align-items:center;gap:var(--ins-s-4);height:24px;padding:0 var(--ins-s-4);color:var(--ins-fg);background:var(--ins-bg);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-md);box-shadow:var(--ins-shadow-sm);white-space:nowrap;font:400 11px/16px var(--ins-font-mono);display:none}.tooltip b{font:600 12px/16px var(--ins-font-sans)}.tooltip .position{color:var(--ins-fg-muted)}.grid{z-index:1;--grid-strength:.12;-webkit-backdrop-filter:invert(var(--grid-strength));backdrop-filter:invert(var(--grid-strength));--grid-lines:linear-gradient(to right, #000 1px, transparent 1px), linear-gradient(to bottom, #000 1px, transparent 1px);-webkit-mask-image:var(--grid-lines);-webkit-mask-image:var(--grid-lines);mask-image:var(--grid-lines);display:none}.grid.is-8{--grid-strength:.22}.launcher{right:calc(var(--ins-dock) + var(--ins-s-6));bottom:var(--ins-s-6);z-index:5;align-items:center;gap:var(--ins-s-3);height:32px;padding:0 var(--ins-s-5) 0 var(--ins-s-4);color:var(--ins-fg);background:var(--ins-bg);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-pill);box-shadow:var(--ins-shadow-sm);white-space:nowrap;font:500 12px/16px var(--ins-font-sans);cursor:pointer;transition:right var(--ins-dur);display:flex;position:fixed}.launcher .icon{color:var(--ins-fg-muted)}.launcher .dot{color:var(--ins-fg-subtle)}.launcher .hold{align-items:center;gap:var(--ins-s-2);color:var(--ins-fg-muted);font-weight:400;display:flex}.launcher .locked,.launcher .off{display:none}.launcher.is-locked{cursor:default;padding-right:var(--ins-s-1);border-color:var(--ins-pin);box-shadow:var(--ins-shadow-sm), 0 0 0 3px var(--ins-pin-tint)}.launcher.is-locked .icon{color:var(--ins-token)}.launcher.is-locked .dot,.launcher.is-locked .hold{display:none}.launcher.is-locked .locked{height:18px;padding:0 var(--ins-s-3);color:var(--ins-token);background:var(--ins-pin-tint);border-radius:var(--ins-radius-pill);font:500 11px/18px var(--ins-font-sans);display:block}.launcher.is-locked .off{width:24px;height:24px;color:var(--ins-fg-muted);border-radius:var(--ins-radius-pill);place-items:center;display:grid}.menu{z-index:6;width:var(--ins-menu-w);max-height:320px;padding:var(--ins-s-2);color:var(--ins-fg);background:var(--ins-bg);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius);box-shadow:var(--ins-shadow);font:400 12px/16px var(--ins-font-mono);position:fixed;overflow:auto}.menu-head{height:var(--ins-row-sm);padding:0 var(--ins-s-3);color:var(--ins-fg-subtle);font:600 11px/16px var(--ins-font-sans);justify-content:space-between;align-items:center;display:flex}.menu-head .count{font:400 11px/16px var(--ins-font-mono)}.menu-item{align-items:center;gap:var(--ins-s-3);height:var(--ins-row);padding:0 var(--ins-s-3);border-radius:var(--ins-radius-sm);white-space:nowrap;cursor:pointer;display:flex}.menu-item:hover{background:var(--ins-bg-hover)}.menu-item .size{color:var(--ins-fg-muted);flex:none;font-size:11px}.panel{z-index:5;width:var(--ins-panel-w);max-width:calc(100vw - 40px);color:var(--ins-fg);background:var(--ins-bg);border-left:1px solid var(--ins-border-strong);box-shadow:var(--ins-shadow);font:400 12px/16px var(--ins-font-sans);flex-direction:column;display:flex;position:fixed;top:0;bottom:0;right:0;container-type:inline-size}.panel:not([data-tab=specs]) [data-for=specs],.panel:not([data-tab=css]) [data-for=css],.panel:not([data-tab=layers]) [data-for=layers],.panel:not([data-tab=changes]) [data-for=changes],.panel:not([data-tab=reference]) [data-for=reference]{display:none}.tab-bar{align-items:center;gap:var(--ins-s-2);height:var(--ins-bar-h);padding:0 var(--ins-s-3) 0 var(--ins-s-4);border-bottom:1px solid var(--ins-border);flex:none;display:flex}.tabs{gap:var(--ins-s-1);min-width:0;padding:var(--ins-s-1);background:var(--ins-bg-raised);border:1px solid var(--ins-border);border-radius:var(--ins-radius);flex:1;grid-auto-columns:minmax(0,1fr);grid-auto-flow:column;display:grid}.tab{height:24px;padding:0 var(--ins-s-1);text-align:center;text-overflow:ellipsis;white-space:nowrap;color:var(--ins-fg-muted);border-radius:var(--ins-radius-md);font:500 12px/24px var(--ins-font-sans);transition:background var(--ins-dur), color var(--ins-dur);overflow:hidden}.tab:hover{color:var(--ins-fg)}.panel[data-tab=specs] .tab[data-tab=specs],.panel[data-tab=css] .tab[data-tab=css],.panel[data-tab=layers] .tab[data-tab=layers],.panel[data-tab=changes] .tab[data-tab=changes]{color:var(--ins-fg);background:var(--ins-bg-active)}.icon-button{width:24px;height:24px;color:var(--ins-fg-muted);border-radius:var(--ins-radius-sm);flex:none;place-items:center;display:grid}.icon-button:hover{color:var(--ins-fg);background:var(--ins-bg-hover)}.more-button[aria-expanded=true],.panel[data-tab=reference] .more-button{color:var(--ins-fg);background:var(--ins-bg-active)}.more-menu{top:calc(var(--ins-bar-h) - var(--ins-s-2));right:var(--ins-s-3);z-index:2;min-width:160px;padding:var(--ins-s-2);background:var(--ins-bg);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius);box-shadow:var(--ins-shadow);flex-direction:column;display:flex;position:absolute}.more-item{height:var(--ins-row);padding:0 var(--ins-s-4);border-radius:var(--ins-radius-sm);font:400 12px/24px var(--ins-font-sans)}.more-item:hover{background:var(--ins-bg-hover)}.reference{column-gap:var(--ins-s-5);row-gap:var(--ins-s-4);margin:var(--ins-s-2) 0 0;grid-template-columns:112px minmax(0,1fr);display:grid}.reference dt{color:var(--ins-fg);font:500 11px/18px var(--ins-font-sans)}.reference dd{color:var(--ins-fg-muted);font:400 11px/18px var(--ins-font-sans);text-wrap:pretty;margin:0}.reference kbd{vertical-align:baseline}.action-bar{align-items:center;gap:var(--ins-s-4);height:var(--ins-bar-h);padding:0 var(--ins-s-5);white-space:nowrap;border-bottom:1px solid var(--ins-border);flex:none;display:flex;overflow:hidden}.segmented{align-items:center;gap:var(--ins-s-1);padding:var(--ins-s-1);background:var(--ins-bg-raised);border:1px solid var(--ins-border);border-radius:var(--ins-radius-md);flex:none;display:flex}.segmented .lead{width:20px;height:20px;color:var(--ins-fg-muted);place-items:center;display:grid}.segmented.is-on .lead{color:var(--ins-token)}.segment{height:20px;padding:0 var(--ins-s-3);color:var(--ins-fg-muted);border-radius:var(--ins-radius-sm);font:400 11px/20px var(--ins-font-mono)}.segment[aria-checked=true]{color:var(--ins-fg);background:var(--ins-bg-active)}.segment.is-on[aria-checked=true]{background:var(--ins-pin-tint);box-shadow:inset 0 0 0 1px var(--ins-pin)}.action{align-items:center;gap:var(--ins-s-2);min-width:26px;height:26px;padding:0 var(--ins-s-3);color:var(--ins-fg-muted);border-radius:var(--ins-radius-md);font:500 11px/16px var(--ins-font-sans);border:1px solid #0000;flex:none;justify-content:center;display:flex}.action:hover{color:var(--ins-fg);background:var(--ins-bg-hover)}.action[aria-pressed=true]{color:var(--ins-fg);background:var(--ins-pin-tint);border-color:var(--ins-pin)}.action[aria-pressed=true] .icon{color:var(--ins-token)}.action .when-on{display:none}.action[aria-pressed=true] .when-on{display:inline}@container (width<=359px){.action[aria-pressed=true] .when-on{display:none}}.action.is-copied{color:var(--ins-ok);background:var(--ins-ok-bg);box-shadow:none;border-radius:var(--ins-radius-md)}.body{scrollbar-width:thin;scrollbar-color:var(--ins-border-strong) transparent;flex:1;min-height:0;overflow:hidden auto}.resize{cursor:ew-resize;width:6px;position:absolute;top:0;bottom:0;left:-3px}.resize:hover{background:var(--ins-pin-tint)}.rail{display:none}.panel.is-collapsed{width:var(--ins-s-7)!important}.panel.is-collapsed>:not(.rail){display:none}.panel.is-collapsed .rail{align-items:center;gap:var(--ins-s-4);padding-top:var(--ins-s-4);color:var(--ins-fg-muted);flex-direction:column;flex:1;display:flex}.rail .name{writing-mode:vertical-rl;font:500 11px/16px var(--ins-font-sans)}.block{padding:var(--ins-s-4) var(--ins-s-5) var(--ins-s-5);border-bottom:1px solid var(--ins-border);flex-direction:column;display:flex}.block-head{justify-content:space-between;align-items:center;gap:var(--ins-s-4);min-height:var(--ins-row-sm);display:flex}.block-head h3,.heading{color:var(--ins-fg-subtle);font:600 11px/16px var(--ins-font-sans);margin:0}.block-head .note{color:var(--ins-fg-subtle);font:400 11px/16px var(--ins-font-sans)}.ghost-button{align-items:center;gap:var(--ins-s-2);height:24px;padding:0 var(--ins-s-4);color:var(--ins-fg);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-md);font:500 11px/16px var(--ins-font-sans);flex:none;display:flex}.ghost-button:hover{background:var(--ins-bg-hover)}.hint{color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);text-wrap:pretty}.element-header{gap:var(--ins-s-1);padding:var(--ins-s-4) var(--ins-s-5);border-bottom:1px solid var(--ins-border);flex-direction:column;flex:none;display:flex}.crumbs{align-items:center;gap:var(--ins-s-2);height:var(--ins-row-sm);white-space:nowrap;min-width:0;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);display:flex}.crumb{text-overflow:ellipsis;min-width:0;overflow:hidden}button.crumb:hover{color:var(--ins-fg)}.crumbs .sep{color:var(--ins-fg-subtle);flex:none}.crumb.is-current{color:var(--ins-fg);flex:none}.title-row{align-items:center;gap:var(--ins-s-3);height:var(--ins-row);display:flex}.tag-chip{padding:0 var(--ins-s-2);color:var(--ins-fg-muted);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-sm);font:400 11px/16px var(--ins-font-mono);flex:none}.title{text-overflow:ellipsis;white-space:nowrap;min-width:0;font:600 13px/16px var(--ins-font-sans);overflow:hidden}.title-size{color:var(--ins-fg-muted);font:400 12px/16px var(--ins-font-mono);flex:none}.class-row{align-items:center;gap:var(--ins-s-2);min-height:var(--ins-row-sm);min-width:0;display:flex}.class-row .classes{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-mono);flex:1;overflow:hidden}.more{align-items:center;gap:var(--ins-s-1);height:18px;padding:0 var(--ins-s-2);color:var(--ins-fg-muted);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-sm);font:500 11px/16px var(--ins-font-sans);flex:none;display:flex}.class-list{align-items:center;gap:var(--ins-s-2);padding:var(--ins-s-1) 0;flex-wrap:wrap;display:flex}.class-chip{padding:0 var(--ins-s-2);color:var(--ins-fg);background:var(--ins-bg-raised);border:1px solid var(--ins-border);border-radius:var(--ins-radius-sm);font:400 11px/16px var(--ins-font-mono)}.class-chip.is-generated{color:var(--ins-fg-muted)}.grid2{column-gap:var(--ins-s-4);grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.cell,.wide{min-height:var(--ins-row);align-items:center;min-width:0;display:flex}.wide{padding:var(--ins-s-2) 0;align-items:flex-start}.cell .label,.wide .label,.size-row .label{width:var(--ins-label-w);color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);flex:none}.value{font:400 12px/16px var(--ins-font-mono);position:relative}.cell .value{white-space:nowrap}.wide .value{overflow-wrap:anywhere;min-width:0}.value.is-copied:after{content:"✓ Copied";z-index:1;margin-left:var(--ins-s-3);padding-right:var(--ins-s-2);color:var(--ins-ok);background:var(--ins-bg);white-space:nowrap;font:500 11px/16px var(--ins-font-sans);position:absolute;top:0;left:100%}.token-note{color:var(--ins-token);font:400 11px/16px var(--ins-font-mono);letter-spacing:-.02em;overflow-wrap:anywhere}.size-row{align-items:center;gap:var(--ins-s-4);height:var(--ins-row);min-width:0;display:flex}.size-row .label{margin-right:calc(-1 * var(--ins-s-4))}.intents{align-items:center;gap:var(--ins-s-2);color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);flex:none;display:flex}.intent{align-items:center;gap:var(--ins-s-1);color:var(--ins-fg);display:flex}.intent.is-height .icon{transform:rotate(90deg)}.box-margin,.box-padding{padding:var(--ins-s-6) var(--ins-s-7);position:relative}.box-margin{color:var(--ins-fg-muted);background:var(--ins-box-margin-bg);border:1px dashed var(--ins-box-margin-line);border-radius:var(--ins-radius-sm);font:400 11px/16px var(--ins-font-mono)}.box-padding{color:var(--ins-fg);background:var(--ins-box-padding-bg);border:1px solid var(--ins-box-padding-line);border-radius:var(--ins-radius-xs)}.box-content{background:var(--ins-box-content-bg);border:1px solid var(--ins-box-content-line);border-radius:2px;place-items:center;height:28px;display:grid}.box-name{left:var(--ins-s-2);color:var(--ins-fg-muted);font:400 10px/16px var(--ins-font-sans);position:absolute;top:0}.box-side{position:absolute}.box-side.is-top,.box-side.is-bottom{left:50%;transform:translate(-50%)}.box-side.is-top{top:0}.box-side.is-bottom{bottom:0}.box-side.is-left,.box-side.is-right{width:var(--ins-s-7);text-align:center;top:50%;transform:translateY(-50%)}.box-side.is-left{left:0}.box-side.is-right{right:0}.token-rows{gap:var(--ins-s-2);flex-direction:column;display:flex}.token-row{margin:0 calc(-1 * var(--ins-s-3));padding:0 var(--ins-s-3) var(--ins-s-1);border-radius:var(--ins-radius-sm);flex-direction:column;display:flex}.token-row:hover{background:var(--ins-bg-hover)}.token-row.is-missing{background:var(--ins-warn-bg)}.token-line{align-items:center;gap:var(--ins-s-3);height:var(--ins-row-sm);min-width:0;display:flex}.token-line .alpha{color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-mono)}.badge{align-items:center;gap:var(--ins-s-1);height:16px;padding:0 var(--ins-s-2);color:var(--ins-fg-muted);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-xs);white-space:nowrap;font:500 10px/14px var(--ins-font-sans);flex:none;display:flex}.badge.is-warn{color:var(--ins-warn);background:var(--ins-warn-bg);border-color:#0000}.role{white-space:nowrap;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);justify-items:end;display:grid}.role>*{align-items:center;gap:var(--ins-s-1);grid-area:1/1;display:flex}.role .copy-hint,.role .copied,.token-row:hover .role .name,.token-row.is-copied .role .name,.token-row.is-copied .role .copy-hint{visibility:hidden}.token-row:hover:not(.is-copied) .role .copy-hint{visibility:visible;color:var(--ins-fg)}.token-row.is-copied .role .copied{visibility:visible;color:var(--ins-ok);font-weight:500}.theme-path{color:var(--ins-token);font:400 12px/18px var(--ins-font-mono);overflow-wrap:anywhere}.css-var{color:var(--ins-token);font:400 11px/16px var(--ins-font-mono);letter-spacing:-.02em;overflow-wrap:anywhere}.css-var.is-secondary{color:var(--ins-fg-muted)}.modes{align-items:center;column-gap:var(--ins-s-4);font:400 11px/16px var(--ins-font-mono);flex-wrap:wrap;display:flex}.mode{align-items:center;gap:var(--ins-s-2);color:var(--ins-fg-muted);display:flex}.mode .scope{font:400 11px/16px var(--ins-font-sans)}.mode.is-active{color:var(--ins-fg)}.mode.is-active .scope{font-weight:500}.warn-text{color:var(--ins-warn);font:400 11px/16px var(--ins-font-sans)}.motion-row{align-items:center;gap:var(--ins-s-3);height:var(--ins-row);min-width:0;font:400 12px/16px var(--ins-font-mono);display:flex}.motion-row .property{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.motion-row .duration{flex:none}.motion-row .easing{color:var(--ins-fg-muted);flex:none}.component{gap:var(--ins-s-2)}.code{padding:var(--ins-s-3) 36px var(--ins-s-3) var(--ins-s-4);color:var(--ins-fg);background:var(--ins-bg-raised);border:1px solid var(--ins-border);border-radius:var(--ins-radius-md);font:400 12px/18px var(--ins-font-mono);overflow-wrap:anywhere;position:relative}.code.is-inside{border:1px dashed var(--ins-border-strong)}.code .name{color:var(--ins-token)}.code.is-copied{background:var(--ins-bg-raised);box-shadow:none;border-radius:var(--ins-radius-md)}.code .copy-mark{right:var(--ins-s-2);top:var(--ins-s-2);width:24px;height:24px;color:var(--ins-fg-muted);border-radius:var(--ins-radius-sm);place-items:center;display:grid;position:absolute}.code .copy-mark .when-copied,.code.is-copied .copy-mark .when-idle{display:none}.code.is-copied .copy-mark{color:var(--ins-ok);background:var(--ins-ok-bg)}.code.is-copied .copy-mark .when-copied{display:block}.props{column-gap:var(--ins-s-5);grid-template-columns:max-content minmax(0,1fr);align-items:baseline;display:grid}.props>*{min-height:var(--ins-row-sm);line-height:var(--ins-row-sm)}.prop-name{color:var(--ins-fg-muted);font:400 11px/22px var(--ins-font-mono)}.props .value{overflow-wrap:anywhere;justify-self:start}.source{gap:var(--ins-s-3);padding-top:var(--ins-s-1);font:400 11px/16px var(--ins-font-mono);display:flex}.source .label{width:44px;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);flex:none}.source .path{min-width:0;color:var(--ins-fg-muted);overflow-wrap:anywhere}.source .file{color:var(--ins-fg)}.select-row{align-items:center;gap:var(--ins-s-3);padding-top:var(--ins-s-2);display:flex}.who{align-items:center;gap:var(--ins-s-3);height:var(--ins-row);min-width:0;display:flex}.letter{width:16px;height:16px;color:var(--ins-bg);background:var(--ins-fg);border-radius:var(--ins-radius-xs);font:600 10px/16px var(--ins-font-sans);flex:none;place-items:center;display:grid}.letter.is-b{color:var(--ins-fg);border:1px solid var(--ins-fg-muted);background:0 0;line-height:14px}.who .name{flex:none;font-weight:500}.who .board{text-overflow:ellipsis;white-space:nowrap;min-width:0;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);overflow:hidden}.compare-summary{height:var(--ins-row-sm);margin-top:var(--ins-s-2);padding-top:var(--ins-s-2);color:var(--ins-fg-muted);border-top:1px solid var(--ins-border);font:400 11px/16px var(--ins-font-sans);align-items:center;display:flex}.compare-row{align-items:center;column-gap:var(--ins-s-3);min-height:var(--ins-row);font:400 12px/16px var(--ins-font-mono);grid-template-columns:96px minmax(0,1fr) 14px minmax(0,1fr);display:grid}.compare-row .label{color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans)}.compare-row .side{align-items:center;gap:var(--ins-s-2);overflow-wrap:anywhere;min-width:0;display:flex}.compare-row .side.is-before,.compare-row .icon{color:var(--ins-fg-muted)}.search{align-items:center;gap:var(--ins-s-3);height:var(--ins-bar-h);padding:0 var(--ins-s-5);color:var(--ins-fg-muted);border-bottom:1px solid var(--ins-border);flex:none;display:flex}.search input{all:unset;min-width:0;color:var(--ins-fg);font:400 12px/16px var(--ins-font-mono);flex:1}.search input::placeholder{color:var(--ins-fg-subtle)}.search input:placeholder-shown+.icon-button{display:none}.switches{justify-content:flex-end;align-items:center;gap:var(--ins-s-5);height:32px;padding:0 var(--ins-s-5);border-bottom:1px solid var(--ins-border);flex:none;display:flex}.switches label{align-items:center;gap:var(--ins-s-3);color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-sans);-webkit-user-select:none;user-select:none;display:flex}.switches input{accent-color:var(--ins-pin);margin:0}.css-body{padding-bottom:var(--ins-s-5)}.css-body .heading{padding:var(--ins-s-5) var(--ins-s-5) var(--ins-s-2)}.css-body .heading.is-custom{color:var(--ins-warn)}.divider{margin:var(--ins-s-4) var(--ins-s-5) 0;border-top:1px solid var(--ins-border)}.row{align-items:center;gap:var(--ins-s-4);min-height:var(--ins-row-sm);margin:0 var(--ins-s-3);padding:0 var(--ins-s-3);border-radius:var(--ins-radius-sm);display:flex}.row:hover{background:var(--ins-bg-hover)}.row.is-overridden{background:var(--ins-warn-bg)}.row .name{text-overflow:ellipsis;white-space:nowrap;width:140px;color:var(--ins-fg-muted);font:400 11px/16px var(--ins-font-mono);flex:none;overflow:hidden}.row .value{min-width:0;padding:0 var(--ins-s-1);text-overflow:ellipsis;white-space:nowrap;border-radius:var(--ins-radius-xs);outline:none;flex:1;overflow:hidden}.row .value[contenteditable]{cursor:text}.row .value:focus{white-space:pre;background:var(--ins-bg-active);box-shadow:0 0 0 1px var(--ins-pin);overflow:visible}.row.is-dimmed .name,.row.is-dimmed .value{color:var(--ins-fg-subtle)}.row.is-overridden .value{color:var(--ins-warn)}.row .reset{padding:0 var(--ins-s-2);color:var(--ins-warn);font:400 11px/16px var(--ins-font-sans);flex:none}.content .heading{padding:0 0 var(--ins-s-2)}.editor{max-height:180px;padding:var(--ins-s-3) var(--ins-s-4);white-space:pre-wrap;overflow-wrap:anywhere;cursor:text;color:var(--ins-fg);background:var(--ins-bg-raised);border:1px solid var(--ins-border);border-radius:var(--ins-radius-md);font:400 12px/18px var(--ins-font-mono);outline:none;overflow-y:auto}.editor:focus{border-color:var(--ins-pin)}.footer{padding-top:var(--ins-s-3);justify-content:space-between;align-items:center;display:flex}.footer .reset{color:var(--ins-warn);font:500 11px/16px var(--ins-font-sans)}.tree{padding:var(--ins-s-3);font:400 12px/16px var(--ins-font-mono)}.layer{align-items:center;gap:var(--ins-s-2);height:var(--ins-row);min-width:0;padding:0 var(--ins-s-3) 0 calc(var(--ins-s-2) + var(--depth,0) * var(--ins-indent));border-radius:var(--ins-radius-sm);white-space:nowrap;cursor:pointer;display:flex}.layer:hover{background:var(--ins-bg-hover)}.layer.is-current{background:var(--ins-pin-tint);box-shadow:inset 2px 0 0 var(--ins-pin)}.layer .caret{width:12px;height:12px;color:var(--ins-fg-muted);flex:none;place-items:center;display:grid}.layer-name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.layer-name .classes{color:var(--ins-fg-muted)}.changes-head{gap:var(--ins-s-3);padding:var(--ins-s-4) var(--ins-s-5);border-bottom:1px solid var(--ins-border);flex-direction:column;flex:none;display:flex}.file-button{align-items:center;gap:var(--ins-s-3);min-width:0;height:28px;padding:0 var(--ins-s-4);color:var(--ins-fg);background:var(--ins-bg-raised);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-md);font:400 12px/16px var(--ins-font-mono);display:flex}.file-button .icon{color:var(--ins-fg-muted)}.file-button .file-name{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.file-button .change{color:var(--ins-fg-muted);font:500 11px/16px var(--ins-font-sans);flex:none}.file-button.is-error{border-color:var(--ins-removed-line)}.dates{align-items:center;gap:var(--ins-s-3);font:400 11px/16px var(--ins-font-sans);display:flex}.dates .icon{color:var(--ins-fg-muted)}.changes-list{padding:var(--ins-s-3)}.change-group{align-items:center;gap:var(--ins-s-2);height:var(--ins-row);padding:0 var(--ins-s-2);font:600 11px/16px var(--ins-font-sans);display:flex}.change-group+.change-row,.change-lines+.change-group,.change-row+.change-group{margin-top:0}.change-group:not(:first-child){margin-top:var(--ins-s-2)}.change-group .icon{color:var(--ins-fg-muted)}.count{padding:0 var(--ins-s-2);color:var(--ins-fg-muted);border:1px solid var(--ins-border-strong);border-radius:var(--ins-radius-pill);font:400 10px/14px var(--ins-font-mono)}.change-row{align-items:center;gap:var(--ins-s-2);height:var(--ins-row);padding:0 var(--ins-s-2) 0 calc(var(--ins-s-2) + var(--ins-indent));border-radius:var(--ins-radius-sm);white-space:nowrap;font:400 12px/16px var(--ins-font-mono);display:flex}button.change-row{width:100%}button.change-row:hover,.change-row[aria-expanded=true]{background:var(--ins-bg-raised)}.change-row[aria-expanded=true]{border-radius:var(--ins-radius-sm) var(--ins-radius-sm) 0 0}.change-row .icon{color:var(--ins-fg-muted)}.change-row .board-name{text-overflow:ellipsis;flex:1;min-width:0;overflow:hidden}.change-row .prefix,.change-row .zero{color:var(--ins-fg-muted);font-size:11px}.change-row .totals{gap:var(--ins-s-3);flex:none;font-size:11px;display:flex}.sign{text-align:center;width:12px;font:400 11px/16px var(--ins-font-mono);flex:none}.is-added>.sign,.totals .added{color:var(--ins-added)}.is-removed>.sign,.totals .removed{color:var(--ins-removed)}.change-row.is-removed .board-name{color:var(--ins-fg-muted);text-decoration:line-through}.change-lines{margin-bottom:var(--ins-s-1);padding:var(--ins-s-1) 0 var(--ins-s-2);background:var(--ins-bg-raised);border-radius:0 0 var(--ins-radius-sm) var(--ins-radius-sm);font:400 11px/16px var(--ins-font-mono);flex-direction:column;display:flex}.change-line{gap:var(--ins-s-3);padding:var(--ins-s-1) var(--ins-s-3) var(--ins-s-1) calc(var(--ins-s-2) + 2 * var(--ins-indent));display:flex}.change-line.is-added{background:var(--ins-added-bg)}.change-line.is-removed{background:var(--ins-removed-bg)}.change-line .text{overflow-wrap:anywhere;min-width:0}.changes-empty{align-items:flex-start;gap:var(--ins-s-3);padding:var(--ins-s-6) var(--ins-s-5);flex-direction:column;display:flex}.changes-empty b,.alert b{font:600 12px/16px var(--ins-font-sans)}.primary-button{align-items:center;gap:var(--ins-s-3);height:28px;margin-top:var(--ins-s-2);padding:0 var(--ins-s-5) 0 var(--ins-s-4);color:var(--ins-bg);background:var(--ins-fg);border-radius:var(--ins-radius-md);font:500 12px/16px var(--ins-font-sans);display:flex}.alert{gap:var(--ins-s-3);margin:var(--ins-s-4) var(--ins-s-5);padding:var(--ins-s-4);background:var(--ins-removed-bg);border:1px solid var(--ins-removed-line);border-radius:var(--ins-radius-md);display:flex}.alert .icon{color:var(--ins-removed);margin-top:1px}.alert div{gap:var(--ins-s-1);flex-direction:column;min-width:0;display:flex}`,we=`groma-inspector`,Te=null;function Ee(){return Te||(Te=document.body.appendChild(document.createElement(we)),Te.attachShadow({mode:`open`}).appendChild(v(`style`,``,Ce))),Te}function S(e){return Ee().shadowRoot.appendChild(e),e}function De(e,t){Ee().style.setProperty(e,t)}function C(e){return e instanceof Element&&e.localName===we}function Oe(e){document.body?e():document.addEventListener(`DOMContentLoaded`,e,{once:!0})}var w=[`top`,`right`,`bottom`,`left`];function T(e,t){let n=t===`border`?`-width`:``,r=r=>parseFloat(e.getPropertyValue(`${t}-${r}${n}`))||0;return{top:r(`top`),right:r(`right`),bottom:r(`bottom`),left:r(`left`)}}var ke=null,Ae=()=>ke??=S(v(`div`,`overlay`));function E(e,t){e.width<=0||e.height<=0||y(Ae().appendChild(v(`div`,t)),e)}function je(e,t,n,r){let i=Ae().appendChild(v(`div`,`measure-label`,e));i.style.left=`${t}px`,i.style.top=`${n}px`,i.style.transform=r}function Me(){ke?.replaceChildren()}function Ne(e,t,n){let{top:r,right:i,bottom:a,left:o}=t,s=e.height-r-a,c=e.left+e.width-i,l=e.top+e.height-a;return E({left:e.left,top:e.top,width:e.width,height:r},n),E({left:e.left,top:l,width:e.width,height:a},n),E({left:e.left,top:e.top+r,width:o,height:s},n),E({left:c,top:e.top+r,width:i,height:s},n),{left:e.left+o,top:e.top+r,width:e.width-o-i,height:s}}function Pe(e,t,n){let r=b(e,t),i=e=>({top:e.top*r.y,right:e.right*r.x,bottom:e.bottom*r.y,left:e.left*r.x}),a=i(T(n,`margin`));Ne({left:t.left-a.left,top:t.top-a.top,width:t.width+a.left+a.right,height:t.height+a.top+a.bottom},a,`margin`),E(Ne(Ne(t,i(T(n,`border`)),`border`),i(T(n,`padding`)),`padding`),`content-box`)}function Fe(e,t,n){let r=Array.from(e.children).filter(e=>{if(C(e))return!1;let t=getComputedStyle(e);return t.display!==`none`&&t.visibility!==`hidden`}).map(e=>e.getBoundingClientRect());if(r.forEach(e=>E(e,`child`)),!n.display.includes(`flex`))return;let i=n.flexDirection.startsWith(`row`);r.sort((e,t)=>i?e.left-t.left:e.top-t.top);for(let e=0;e<r.length-1;e++){let n=r[e],a=r[e+1];E(i?{left:n.right,top:t.top,width:a.left-n.right,height:t.height}:{left:t.left,top:n.bottom,width:t.width,height:a.top-n.bottom},`gap`)}}var Ie={x:32,y:22},Le={x:[`translate(-50%, -150%)`,`translate(-50%, 50%)`],y:[`translate(8px, -50%)`,`translate(calc(-100% - 8px), -50%)`]},Re=(e,t)=>e.left<=t.left&&e.right>=t.right&&e.top<=t.top&&e.bottom>=t.bottom;function ze(e,t,n,r){let i=Math.max(e,n),a=Math.min(t,r);return i<a?(i+a)/2:(e+t)/2}var Be=e=>String(Math.round(e*10)/10);function Ve(e,t,n){let r=ze(e.top,e.bottom,t.top,t.bottom),i=ze(e.left,e.right,t.left,t.right),a=[[Math.min(e.left,t.left),Math.max(e.left,t.left),`x`],[Math.min(e.right,t.right),Math.max(e.right,t.right),`x`],[Math.min(e.top,t.top),Math.max(e.top,t.top),`y`],[Math.min(e.bottom,t.bottom),Math.max(e.bottom,t.bottom),`y`]],o=[[e.right,t.left,`x`],[t.right,e.left,`x`],[e.bottom,t.top,`y`],[t.bottom,e.top,`y`]],s={x:0,y:0};(Re(e,t)||Re(t,e)?a:o).forEach(([e,t,a])=>{let o=t-e;if(o<.5)return;let c=e+o/2,l=o>=Ie[a]?`translate(-50%, -50%)`:Le[a][s[a]++%2];a===`x`?(E({left:e,top:r,width:o,height:1},`measure is-x`),je(Be(o/n.x),c,r,l)):(E({left:i,top:e,width:1,height:o},`measure is-y`),je(Be(o/n.y),i,c,l))})}var He=`.sa-stage, .dc-card, [data-artboard]`;function Ue(e){return e.parentElement?.closest(He)??null}function D(e,t){let n=Ue(e);if(!n)return null;let r=n.getBoundingClientRect(),i=b(n,r),a=e=>Math.round(e*10)/10,o=a((t.left-r.left)/i.x),s=a((t.top-r.top)/i.y);return{x:o,y:s,text:`x ${o} · y ${s}`,board:n.closest(`[aria-label]`)?.getAttribute(`aria-label`)??``}}var We=5,Ge=new Set([`class`,`style`,`id`]),Ke=e=>{let t=e;for(let n=0;t&&n<=We;t=t.parentElement,n++){if(!t.localName.includes(`-`))continue;let n=Object.fromEntries(Array.from(t.attributes).filter(e=>!Ge.has(e.name)).map(e=>[e.name,e.value===``||e.value]));return{name:t.localName,inside:t!==e,element:t,props:n}}return null};function qe(e){try{let t=JSON.parse(e.getAttribute(`data-props`)??`{}`);return t&&typeof t==`object`?t:{}}catch{return{}}}var Je=e=>{let t=e.closest(`[data-component]`),n=t?.getAttribute(`data-component`);return!t||!n?null:{name:n,sourcePath:t.getAttribute(`data-source`)??void 0,inside:t!==e,element:t,props:qe(t)}},O=null;function Ye(){if(O)return O;O=new Map;for(let e of document.querySelectorAll(`script[data-src]`)){let t=/^\/\*\s*@ds-bundle:\s*(\{.*?\})\s*\*\//s.exec((e.textContent??``).slice(0,2e5));if(t)try{let{namespace:e,components:n}=JSON.parse(t[1]),r=window[e];n.forEach(e=>{r?.[e.name]&&O.set(r[e.name],e)})}catch{}}return O}function Xe(e){let t=Object.keys(e).find(e=>e.startsWith(`__reactFiber$`));return t?e[t]:null}function Ze(e){let t=e;for(;t&&typeof t.type!=`string`;)t=t.child;return t?.stateNode instanceof Element?t.stateNode:void 0}var Qe=[Je,e=>{let t=Ye(),n=null,r=!1,i=Xe(e)?.return??null;for(let e=0;i&&e<40;i=i.return,e++){let e=i.type;if(typeof e==`string`&&(r=!0),!e||typeof e==`string`)continue;let a=i.memoizedProps??{},o=t.get(e);if(o)return{...o,inside:r,props:a,element:Ze(i)};let{displayName:s,name:c,styledComponentId:l}=e,u=(s??c??``).replace(/\d+$/,``),d=/^[A-Z]/.test(u)&&!/^Component$|Provider$/.test(u);!n&&!r&&!l&&d&&(n={name:u,inside:r,props:a,element:Ze(i)})}return n},Ke];function $e(e){for(let t of Qe)try{let n=t(e);if(n)return n}catch{}return null}function et({props:e}){return Object.entries(e).filter(([e])=>![`children`,`className`,`style`].includes(e)).filter(([,e])=>[`string`,`number`,`boolean`].includes(typeof e)).map(([e,t])=>({name:e,value:String(t)}))}function k(e){let t=$e(e);return t&&!t.inside?t.name:e.localName}function A(e,t=e.getBoundingClientRect()){let{width:n,height:r}=ge(e,t);return`${n} × ${r}`}function tt(e,t){let n=document.createElement(`span`);n.className=t,n.textContent=e.localName;let r=e.classList[0];if(r){let e=n.appendChild(document.createElement(`span`));e.className=`classes`,e.textContent=`.`+r}return n}var nt=6,rt=4,j=null;function it(e,t){j??=S(v(`div`,`tooltip`));let n=D(e,t)?.text??``;j.replaceChildren(v(`b`,``,k(e)),v(`span`,``,A(e,t)),v(`span`,`position`,n)),j.style.display=`flex`;let r=t.top-j.offsetHeight-nt,i=r>=rt?r:t.top+t.height+nt,a=window.innerWidth-j.offsetWidth-rt;j.style.top=`${i}px`,j.style.left=`${Math.max(rt,Math.min(t.left,a))}px`}function at(){j&&(j.style.display=`none`)}var ot=!1,st=!1,ct=null,M=()=>ot||st;function lt(){Me(),at(),ct=null}function ut(){document.body.style.cursor=M()?`crosshair`:``,M()||lt()}function dt(e){st=e,ut()}function ft(e){if(e===ct||C(e))return;if(!(e instanceof Element)||e===document.body||e===document.documentElement){lt();return}Me(),ct=e;let t=e.getBoundingClientRect(),r=n();if(r&&r!==e&&r.isConnected){let e=r.getBoundingClientRect();E(t,`target`),Ve(e,t,b(r,e))}else{let n=getComputedStyle(e);Pe(e,t,n),Fe(e,t,n)}it(e,t)}function pt(){let e=e=>{ot=e,ut()};document.addEventListener(`keydown`,t=>{t.key===`Alt`&&e(!0)}),document.addEventListener(`keyup`,t=>{t.key===`Alt`&&e(!1)}),window.addEventListener(`blur`,()=>e(!1)),document.addEventListener(`mousemove`,e=>{M()&&ft(e.target)},!0),document.addEventListener(`mouseleave`,()=>{M()&&lt()});for(let e of[`pointerdown`,`pointerup`,`mousedown`,`mouseup`,`click`])document.addEventListener(e,t=>{M()&&!C(t.target)&&(t.preventDefault(),t.stopPropagation(),e===`click`&&t.target instanceof Element&&a(t.target))},!0)}var mt=/Mac|iPhone|iPad/.test(navigator.platform),ht=mt?`⌥`:`Alt`,gt=mt?`⌘`:`Ctrl`;function _t(){Oe(()=>{let e=S(v(`div`,`launcher`));e.setAttribute(`role`,`button`),e.title=`Lock inspect mode on`;let t=v(`span`,`hold`,`hold`);t.appendChild(v(`kbd`,``,ht));let n=v(`button`,`off`);n.setAttribute(`aria-label`,`Turn off inspect`),n.appendChild(x(`close`,12)),e.append(x(`inspect`),v(`span`,``,`Inspect`),v(`span`,`dot`,`·`),t,v(`span`,`locked`,`Locked on`),n);let r=t=>{e.classList.toggle(`is-locked`,t),dt(t)};e.addEventListener(`click`,()=>r(!0)),n.addEventListener(`click`,e=>{e.stopPropagation(),r(!1)}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&r(!1)})})}function vt(e){let t=[];for(let n=e;n.parentElement&&n!==document.body;n=n.parentElement){let e=n.getAttribute(`id`);if(e)return[`#`+CSS.escape(e),...t].join(` > `);let r=Array.from(n.parentElement.children).filter(e=>e.localName===n.localName),i=Array.from(n.classList,e=>`.`+CSS.escape(e)).join(``),a=r.length>1?`:nth-of-type(${r.indexOf(n)+1})`:``;t.unshift(CSS.escape(n.localName)+i+a)}return[`body`,...t].join(` > `)}var yt=`inspect`;function bt(e){let t=new URL(location.href);t.searchParams.set(yt,vt(e));let n=e.closest(`section[id]`);return n&&(t.hash=n.id),t.href}function xt(){let e=new URLSearchParams(location.search).get(yt);if(!e)return;let t=0,n=setInterval(()=>{let r=null;try{r=document.querySelector(e)}catch{}r&&!C(r)&&(r.scrollIntoView({block:`center`}),a(r)),(r||++t>60)&&clearInterval(n)},250)}var N={unit:`px`,gridStep:0,highlightPinned:!0,hideDefaults:!0,search:``,compareWith:null,pickedColor:``},St=18,P=null,Ct=null,F=null,I=null,L=null,wt=0;function Tt(e){I??=S(v(`div`,`grid`));let t=Ue(e)??document.body,n=t.getBoundingClientRect(),r=b(t,n),i=he(t,n);y(I,i);let a=`${n.left-i.left}px ${n.top-i.top}px`;I.style.setProperty(`-webkit-mask-position`,a),I.style.setProperty(`mask-position`,a),I.classList.toggle(`is-8`,N.gridStep===8);let o=`${N.gridStep*r.x}px ${N.gridStep*r.y}px`;I.style.setProperty(`-webkit-mask-size`,o),I.style.setProperty(`mask-size`,o)}function Et(){let e=n(),t=N.gridStep>0&&e!==null;if(L&&P){let e=L.getBoundingClientRect();y(P,e),Ct?.classList.toggle(`is-inside`,e.top<St),P.style.visibility=L.isConnected?``:`hidden`}t&&Tt(e),I&&(I.style.display=t?`block`:`none`),wt=L||t?requestAnimationFrame(Et):0}function Dt(){wt||=requestAnimationFrame(Et)}function Ot(e){N.highlightPinned&&(P||(P=S(v(`div`,`outline outline-pinned`)),Ct=P.appendChild(v(`div`,`pin-tag`))),Ct?.replaceChildren(v(`b`,``,k(e)),A(e)),P.style.display=`block`,L=e,Dt())}function kt(){let e=n();e&&N.highlightPinned?Ot(e):(P&&(P.style.display=`none`),L=null),Dt()}function At(e){F??=S(v(`div`,`outline outline-hover`)),y(F,e.getBoundingClientRect()),F.style.display=`block`}function jt(){F&&(F.style.display=`none`)}function Mt(){r(()=>{kt(),jt()})}var R=8,z=null;function Nt(){z&&(z.remove(),z=null,jt())}function Pt(e,t){Nt();let n=document.elementsFromPoint(e,t).filter(e=>!C(e)&&e!==document.body&&e!==document.documentElement),r=z=S(v(`div`,`menu`));r.setAttribute(`role`,`menu`),r.appendChild(v(`div`,`menu-head`)).append(v(`span`,``,`Layers at pointer`),v(`span`,`count`,String(n.length)));for(let e of n){let t=r.appendChild(v(`div`,`menu-item`));t.setAttribute(`role`,`menuitem`),t.append(tt(e,`layer-name`),v(`span`,`size`,A(e))),t.addEventListener(`mouseenter`,()=>At(e)),t.addEventListener(`click`,()=>{Nt(),a(e)})}let i=Math.min(e+R,window.innerWidth-r.offsetWidth-R),o=Math.min(t+R,window.innerHeight-r.offsetHeight-R);r.style.left=`${i}px`,r.style.top=`${o}px`}function Ft(){document.addEventListener(`contextmenu`,e=>{M()&&!C(e.target)&&(e.preventDefault(),e.stopPropagation(),Pt(e.clientX,e.clientY))},!0),document.addEventListener(`mousedown`,e=>{z&&!e.composedPath().includes(z)&&Nt()},!0)}var It=/<script type="application\/json" id="dc-data">([\s\S]*?)<\/script>/;function Lt(e){try{let t=JSON.parse(e??``);return t&&typeof t.sources==`object`&&Array.isArray(t.boards)?t:null}catch{return null}}var Rt=e=>Lt(It.exec(e)?.[1]),zt=()=>Lt(document.getElementById(`dc-data`)?.textContent);function Bt(e){let t=new Map;for(let n of e.split(`
2
+ `)){let e=n.trim();e&&t.set(e,(t.get(e)??0)+1)}return t}function Vt(e,t){return[...e].filter(([e,n])=>n>(t.get(e)??0)).map(([e])=>e)}function Ht(e,t){let n=(e,t)=>e.boards.find(e=>e.file===t),r=(e,t)=>n(e,t)?.title??t,i=Object.keys(t.sources);return{changed:i.filter(n=>n in e.sources&&e.sources[n]!==t.sources[n]).map(i=>{let a=Bt(e.sources[i]),o=Bt(t.sources[i]);return{title:r(t,i),id:n(t,i)?.id,added:Vt(o,a),removed:Vt(a,o)}}),added:i.filter(t=>!(t in e.sources)).map(e=>r(t,e)),removed:Object.keys(e.sources).filter(e=>!(e in t.sources)).map(t=>r(e,t))}}function B(e,t){let n=e.querySelector(t);if(!n)throw Error(`groma: panel.html has no "${t}"`);return n}var V=(e,t)=>B(e,`.body[data-for="${t}"]`),Ut=30,Wt=160,H,U,W,Gt=()=>W.click();function Kt(e){let t=v(`span`,`board-name`),[n,...r]=e.split(` · `);return r.length?t.append(v(`span`,`prefix`,n+` · `),r.join(` · `)):t.textContent=e,t}function qt(e,t){let n=v(`button`,t?`file-button is-error`:`file-button`);return n.append(x(`file`),v(`span`,`file-name`,e),v(`span`,`change`,`Change`)),n.addEventListener(`click`,Gt),n}function Jt(e,t){U.appendChild(v(`div`,`change-group`)).append(x(`chevronDown`,12),e,v(`span`,`count`,String(t)))}function Yt(e){let t=U.appendChild(v(`button`,`change-row`));t.setAttribute(`aria-expanded`,`false`);let n=t.appendChild(v(`span`,`caret`));n.appendChild(x(`chevronRight`,12)),t.appendChild(Kt(e.title));let r=t.appendChild(v(`span`,`totals`)),i=(e,t,n)=>r.appendChild(v(`span`,e?n:`zero`,t+e));i(e.added.length,`+`,`added`),i(e.removed.length,`−`,`removed`);let a=U.appendChild(v(`div`,`change-lines`));a.hidden=!0;let o=(e,t,n)=>e.slice(0,Ut).forEach(e=>{a.appendChild(v(`div`,`change-line ${t}`)).append(v(`span`,`sign`,n),v(`span`,`text`,e.slice(0,Wt)))});o(e.added,`is-added`,`+`),o(e.removed,`is-removed`,`−`),t.addEventListener(`click`,()=>{a.hidden=!a.hidden,t.setAttribute(`aria-expanded`,String(!a.hidden)),n.replaceChildren(x(a.hidden?`chevronRight`:`chevronDown`,12)),e.id&&(location.hash=e.id)})}function Xt(e,t,n){U.appendChild(v(`div`,`change-row ${t}`)).append(v(`span`,`sign`,n),Kt(e))}function Zt(){H.hidden=!0;let e=v(`div`,`changes-empty`),t=v(`button`,`primary-button`);t.append(x(`file`),`Choose export`),t.addEventListener(`click`,Gt),e.append(v(`b`,``,`No export loaded`),v(`span`,`hint`,`Pick an earlier export of this page to see which boards changed.`),t),U.replaceChildren(e)}function Qt(e){H.hidden=!1,H.replaceChildren(qt(e,!0));let t=v(`div`,`alert`);t.setAttribute(`role`,`alert`);let n=v(`div`);n.append(v(`b`,``,`Not an export`),v(`span`,`hint`,`This file has no inspector data. Pick an .html file saved with Export from this page.`)),t.append(x(`warning`),n),U.replaceChildren(t)}function $t(e,t,n){let r=Ht(t,n);H.hidden=!1;let i=v(`div`,`dates`);i.append(v(`span`,``,t.exported),x(`arrow`,12),v(`span`,``,n.exported)),H.replaceChildren(qt(e,!1),i),U.replaceChildren(),r.changed.length&&Jt(`Changed`,r.changed.length),r.changed.forEach(Yt),r.added.length&&Jt(`Added`,r.added.length),r.added.forEach(e=>Xt(e,`is-added`,`+`)),r.removed.length&&Jt(`Removed`,r.removed.length),r.removed.forEach(e=>Xt(e,`is-removed`,`−`)),U.childElementCount||U.appendChild(v(`div`,`changes-empty`)).appendChild(v(`span`,`hint`,`No board changed.`))}var en={mount(e){H=B(e,`.changes-head`),U=V(e,`changes`),W=B(e,`input[type="file"]`),B(e,`.tab[data-tab="changes"]`).hidden=!zt(),W.addEventListener(`change`,async()=>{let e=W.files?.[0];if(W.value=``,!e)return;let t=Rt(await e.text()),n=zt();t&&n?$t(e.name,t,n):Qt(e.name)}),Zt()},render(){}},G=null,tn=new Map;function nn(e){let t=tn.get(e);if(t===void 0){G??=document.createElement(`canvas`).getContext(`2d`),G.fillStyle=`#000`,G.fillStyle=e;let n=G.fillStyle;G.fillStyle=`#fff`,G.fillStyle=e,t=n===G.fillStyle?n:null,tn.set(e,t)}return t}function rn(e,t){let n=nn(t);return Array.from(e).filter(e=>e.startsWith(`--`)).filter(t=>nn(e.getPropertyValue(t).trim())===n).sort((e,t)=>e.length-t.length)}function an(e){let t=/^rgba?\((\d+), (\d+), (\d+)(?:, ([\d.]+))?\)$/.exec(e);if(!t)return e;let n=t.slice(1,4).map(e=>Number(e).toString(16).padStart(2,`0`)).join(``).toUpperCase(),r=t[4]===void 0?``:` ${Math.round(Number(t[4])*100)}%`;return`#`+n+r}var on=e=>/^(#|rgb|rgba|hsl|hsla|oklch|color\()/.test(e.trim()),sn=[{label:`Display & Position`,props:[`display`,`position`,`top`,`right`,`bottom`,`left`,`z-index`,`visibility`,`overflow`,`overflow-x`,`overflow-y`]},{label:`Box Model`,props:[`width`,`height`,`min-width`,`max-width`,`min-height`,`max-height`,`box-sizing`,`margin-top`,`margin-right`,`margin-bottom`,`margin-left`,`border-top-width`,`border-right-width`,`border-bottom-width`,`border-left-width`,`border-style`,`border-color`,`border-radius`,`padding-top`,`padding-right`,`padding-bottom`,`padding-left`]},{label:`Flex (container)`,props:[`flex-direction`,`flex-wrap`,`justify-content`,`align-items`,`align-content`,`gap`,`row-gap`,`column-gap`]},{label:`Flex (item)`,props:[`flex`,`flex-grow`,`flex-shrink`,`flex-basis`,`align-self`,`justify-self`,`order`]},{label:`Grid (container)`,props:[`grid-template-columns`,`grid-template-rows`,`grid-template-areas`,`grid-auto-columns`,`grid-auto-rows`,`grid-auto-flow`]},{label:`Grid (item)`,props:[`grid-column`,`grid-row`,`grid-area`]},{label:`Typography`,props:[`font-family`,`font-size`,`font-weight`,`font-style`,`line-height`,`letter-spacing`,`text-align`,`text-transform`,`text-decoration`,`white-space`,`color`]},{label:`Background`,props:[`background-color`,`background-image`,`background-size`,`background-position`,`background-repeat`]},{label:`Transform & Transition`,props:[`transform`,`transform-origin`,`transition`,`animation`,`opacity`,`pointer-events`]}],cn={position:`static`,top:`auto`,right:`auto`,bottom:`auto`,left:`auto`,"z-index":`auto`,overflow:`visible`,"overflow-x":`visible`,"overflow-y":`visible`,visibility:`visible`,"flex-direction":`row`,"flex-wrap":`nowrap`,"justify-content":`normal`,"align-items":`normal`,"align-content":`normal`,"flex-grow":`0`,"flex-shrink":`1`,"flex-basis":`auto`,"align-self":`auto`,"justify-self":`auto`,order:`0`,"grid-template-columns":`none`,"grid-template-rows":`none`,"grid-template-areas":`none`,"grid-auto-columns":`auto`,"grid-auto-rows":`auto`,"grid-auto-flow":`row`,"grid-column":`auto`,"grid-row":`auto`,"grid-area":`auto`,transform:`none`,animation:`none`,"pointer-events":`auto`,"text-decoration":`none solid rgb(0, 0, 0)`,"text-transform":`none`,"font-style":`normal`,"letter-spacing":`normal`,"background-image":`none`},K,ln=``;function un(e){return[{name:`tag`,value:e.localName},{name:`id`,value:e.id?`#`+e.id:`—`},{name:`classes`,value:Array.from(e.classList).join(` `)||`—`}]}function dn(e){let t=e.getBoundingClientRect();return[`top`,`right`,`bottom`,`left`,`width`,`height`].map(e=>({name:e,value:t[e].toFixed(2)+`px`}))}function fn(e){return Array.from(e).filter(e=>e.startsWith(`--`)).map(t=>({name:t,value:e.getPropertyValue(t).trim()}))}function pn(e){let t=v(`div`,`row`);t.classList.toggle(`is-overridden`,!!e.overridden),t.classList.toggle(`is-dimmed`,!!e.dimmed&&!e.overridden),t.appendChild(v(`span`,`name`,e.name)),on(e.value)&&t.appendChild(v(`span`,`swatch`)).style.setProperty(`--swatch`,e.value);let n=t.appendChild(v(`span`,`value`,e.value||`—`));return n.title=e.value,{rowEl:t,valueEl:n}}var mn=e=>pn(e).rowEl;function hn(e,t,n){let{rowEl:r,valueEl:i}=pn(e),a=e.value,o=!!e.overridden,c=!1;i.contentEditable=`true`,i.spellcheck=!1,i.dataset.row=(e.custom?`custom:`:``)+e.name;let l=r=>{r?(ie(n,e.name,r),ae(t,e.name,r),o=!0,a=r):o&&(ie(n,e.name,null),oe(t,e.name))};if(i.addEventListener(`focus`,()=>{a||(i.textContent=``)}),i.addEventListener(`blur`,e=>{let t=(i.textContent??``).trim();if(t===a&&!c){i.textContent=a||`—`;return}t!==a&&l(t);let n=e.relatedTarget;ln=n instanceof HTMLElement?n.dataset.row??``:``,s()}),i.addEventListener(`keydown`,e=>{e.key===`Enter`&&(e.preventDefault(),i.blur()),e.key===`Escape`&&(i.textContent=a,i.blur());let t=/^(-?\d*\.?\d+)(.*)/.exec(i.textContent??``);if(t&&(e.key===`ArrowUp`||e.key===`ArrowDown`)){e.preventDefault();let n=e.shiftKey?10:e.altKey?.1:1,r=parseFloat(t[1])+(e.key===`ArrowUp`?n:-n),a=n===.1||t[1].includes(`.`)?1:0;i.textContent=r.toFixed(a)+t[2],l(i.textContent),c=!0}}),o){let e=r.appendChild(v(`button`,`reset`,`↩`));e.title=`Reset to the page's own value`,e.addEventListener(`click`,()=>{l(``),s()})}return r}var gn=new Set(`span p h1 h2 h3 h4 h5 h6 label button a li td th caption legend figcaption blockquote cite code pre time mark strong em small b i u s`.split(` `));function _n(e,t){if(!g(e)&&!gn.has(e.localName)){let t=v(`section`,`block`);return t.appendChild(v(`span`,`hint`,`${e.localName} holds other elements, so its content is not editable here.`)),t}let n=v(`section`,`block content`);n.appendChild(v(`div`,`heading`,g(e)?`Value`:`Content`));let r=n.appendChild(v(`div`,`editor`,ce(e)));r.contentEditable=`true`,r.spellcheck=!1;let i=n.appendChild(v(`div`,`footer`));i.appendChild(v(`span`,`hint`,`⌘/Ctrl ↵ to apply · Esc to cancel`));let a=i.appendChild(v(`button`,`reset`,`↩ Reset`));return a.hidden=!ue(t),r.addEventListener(`keydown`,n=>{if(n.key===`Enter`&&(n.metaKey||n.ctrlKey)){n.preventDefault();let i=r.textContent??``;fe(e,i),de(t,i),a.hidden=!1,r.blur()}n.key===`Escape`&&(r.textContent=ce(e),r.blur())}),a.addEventListener(`click`,()=>{de(t,null),pe(e),s()}),n}function vn(e,t){let n=t===`pinned`?0:K.scrollTop,r=vt(e),i=re(r),a=getComputedStyle(e),o=(e,t,n=``)=>{t.length&&(K.appendChild(v(`div`,`heading ${n}`,e)),K.append(...t))},s=e=>!N.search||e.name.toLowerCase().includes(N.search),c=t=>hn(t,e,r);K.replaceChildren(_n(e,r));let l=Object.entries(i).map(([e,t])=>({name:e,value:t,overridden:!0,custom:!0}));o(`Your edits`,l.map(c),`is-custom`),l.length&&K.appendChild(v(`div`,`divider`)),o(`Element`,un(e).filter(s).map(mn)),o(`Rect`,dn(e).filter(s).map(mn));for(let e of sn){let t=e.props.map(e=>{let t=a.getPropertyValue(e).trim(),n=i[e]!==void 0;return{name:e,value:n?i[e]:t,dimmed:cn[e]===t,overridden:n}}).filter(e=>e.value&&s(e)).filter(e=>!(N.hideDefaults&&e.dimmed&&!e.overridden));o(e.label,t.map(c))}if(o(`CSS Variables`,fn(a).filter(s).map(c)),K.scrollTop=n,ln){let e=CSS.escape(ln);ln=``,setTimeout(()=>K.querySelector(`[data-row="${e}"]`)?.focus())}}function yn(e){let t=B(e,`.search input`),n=e=>{t.value=e,N.search=e.toLowerCase().trim(),s()};t.addEventListener(`input`,()=>n(t.value)),t.addEventListener(`keydown`,e=>{e.key===`Escape`&&n(``)}),B(e,`.clear`).addEventListener(`click`,()=>n(``)),B(e,`[data-option="hideDefaults"]`).addEventListener(`change`,e=>{N.hideDefaults=e.target.checked,s()}),B(e,`[data-option="highlightPinned"]`).addEventListener(`change`,e=>{N.highlightPinned=e.target.checked,kt()})}var bn={mount(e){K=V(e,`css`),yn(e)},render:vn},xn=new Set([`script`,`style`,`link`,`meta`,`noscript`]),Sn,Cn=e=>Array.from(e.children).filter(e=>!C(e)&&!xn.has(e.localName));function wn(e,t,n,r){let i=v(`div`),o=Cn(e),s=i.appendChild(v(`div`,`layer`));s.setAttribute(`role`,`treeitem`),s.classList.toggle(`is-current`,e===r),s.style.setProperty(`--depth`,String(t));let c=s.appendChild(v(`span`,`caret`));s.appendChild(tt(e,`layer-name`));let l=i.appendChild(v(`div`)),u=!1,d=e=>{u=e,c.replaceChildren(x(u?`chevronDown`:`chevronRight`,12)),l.replaceChildren(...u?o.map(e=>wn(e,t+1,n,r)):[])};return o.length&&d(n.has(e)),c.addEventListener(`click`,e=>{e.stopPropagation(),o.length&&d(!u)}),s.addEventListener(`mouseenter`,()=>At(e)),s.addEventListener(`mouseleave`,jt),s.addEventListener(`click`,()=>{e!==r&&a(e)}),i}function Tn(e){let t=Ue(e)??document.body,n=new Set;for(let r=e;r&&(r!==e&&n.add(r),r!==t);r=r.parentElement);let r=Sn.scrollTop;Sn.replaceChildren(wn(t,0,n,e)),Sn.scrollTop=r}var En={mount(e){Sn=V(e,`layers`)},render:Tn},Dn=`__groma_panel`,On=[`specs`,`css`,`layers`,`changes`];function kn(e){let t=f(Dn);e.dataset.tab=t.tab&&On.includes(t.tab)?t.tab:`specs`,e.style.width=t.width??``,N.unit=t.unit===`rem`?`rem`:`px`}function An(e){let t=e.dataset.tab??``;p(Dn,{width:e.style.width,tab:On.includes(t)?t:f(Dn).tab??`specs`,unit:N.unit})}function jn(e,t){e.dataset.tab=t,An(e)}var Mn=`<!--
3
+ The side panel. \`data-tab\` on a tab names the tab it opens; \`data-for\` on a
4
+ section names the tab it belongs to (see styles/panel.css). Sections with no
5
+ content here are filled by the tab's TypeScript file.
6
+ -->
7
+ <div class="tab-bar">
8
+ <div class="tabs" role="tablist">
9
+ <button class="tab" role="tab" data-tab="specs">Specs</button>
10
+ <button class="tab" role="tab" data-tab="css">All CSS</button>
11
+ <button class="tab" role="tab" data-tab="layers">Layers</button>
12
+ <button class="tab" role="tab" data-tab="changes">Changes</button>
13
+ </div>
14
+ <button class="icon-button more-button" aria-label="More" aria-haspopup="menu" aria-expanded="false" title="More">
15
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><path d="m6 17 5-5-5-5M13 17l5-5-5-5" /></svg>
16
+ </button>
17
+ <button class="icon-button close" aria-label="Close inspector" title="Close inspector">
18
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12" /></svg>
19
+ </button>
20
+ </div>
21
+
22
+ <!-- The » menu: what does not fit in the tab bar. -->
23
+ <div class="more-menu" role="menu" hidden>
24
+ <button class="more-item" role="menuitem" data-tab="reference">Shortcuts and tips</button>
25
+ <button class="more-item collapse" role="menuitem">Collapse panel</button>
26
+ </div>
27
+
28
+ <!-- ───────────── Specs: what a developer needs to build the element ───────────── -->
29
+ <div class="action-bar" data-for="specs">
30
+ <div class="segmented" role="radiogroup" aria-label="Units" data-option="unit">
31
+ <button class="segment" role="radio" data-value="px">px</button>
32
+ <button class="segment" role="radio" data-value="rem">rem</button>
33
+ </div>
34
+ <div class="segmented" role="radiogroup" aria-label="Grid" data-option="grid">
35
+ <span class="lead">
36
+ <svg class="icon" width="12" height="12" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2" /><path d="M3 9h18M3 15h18M9 3v18M15 3v18" /></svg>
37
+ </span>
38
+ <button class="segment" role="radio" data-value="0">Off</button>
39
+ <button class="segment is-on" role="radio" data-value="4">4</button>
40
+ <button class="segment is-on" role="radio" data-value="8">8</button>
41
+ </div>
42
+ <span class="spacer"></span>
43
+ <button class="action" data-action="compare" aria-pressed="false" aria-label="Compare" title="Compare this element with the next one you pin">
44
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><circle cx="18" cy="18" r="3" /><circle cx="6" cy="6" r="3" /><path d="M13 6h3a2 2 0 0 1 2 2v7M11 18H8a2 2 0 0 1-2-2V9" /></svg>
45
+ <span class="when-on">Comparing</span>
46
+ </button>
47
+ <button class="action" data-action="link" aria-label="Copy link to element" title="Copy a link that reopens this page with the element pinned">
48
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></svg>
49
+ </button>
50
+ <button class="action" data-action="pick" aria-pressed="false" aria-label="Pick colour" title="Pick any colour on screen">
51
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><path d="m2 22 1-1h3l9-9M3 21v-3l9-9" /><path d="m15 6 3.4-3.4a2.1 2.1 0 1 1 3 3L18 9l.4.4a2.1 2.1 0 1 1-3 3l-3.8-3.8a2.1 2.1 0 1 1 3-3l.4.4Z" /></svg>
52
+ </button>
53
+ </div>
54
+ <div class="element-header" data-for="specs"></div>
55
+ <div class="body" data-for="specs"></div>
56
+
57
+ <!-- ───────────── All CSS: every computed property, editable ───────────── -->
58
+ <div class="search" data-for="css">
59
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7" /><path d="m20 20-3.5-3.5" /></svg>
60
+ <input type="text" placeholder="Search properties" />
61
+ <button class="icon-button clear" aria-label="Clear search" title="Clear search">
62
+ <svg class="icon" width="12" height="12" viewBox="0 0 24 24"><path d="M18 6 6 18M6 6l12 12" /></svg>
63
+ </button>
64
+ </div>
65
+ <div class="switches" data-for="css">
66
+ <label><input type="checkbox" data-option="hideDefaults" checked />Hide defaults</label>
67
+ <label><input type="checkbox" data-option="highlightPinned" checked />Outline element</label>
68
+ </div>
69
+ <div class="body css-body" data-for="css"></div>
70
+
71
+ <!-- ───────────── Layers: the artboard's element tree ───────────── -->
72
+ <div class="body tree" data-for="layers" role="tree"></div>
73
+
74
+ <!-- ───────────── Changes: this export against an older one ───────────── -->
75
+ <div class="changes-head" data-for="changes"></div>
76
+ <div class="body changes-list" data-for="changes"></div>
77
+ <input type="file" accept=".html,text/html" hidden />
78
+
79
+ <!-- ───────────── Reference: every shortcut and trick (reference.html) ───────────── -->
80
+ <div class="body" data-for="reference"></div>
81
+
82
+ <!-- Shown instead of everything above while the panel is collapsed. -->
83
+ <button class="rail" aria-label="Expand panel" title="Expand panel">
84
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8" /><path d="M12 2v4M12 18v4M2 12h4M18 12h4" /></svg>
85
+ <span class="name"></span>
86
+ </button>
87
+
88
+ <div class="resize"></div>
89
+ `,Nn=`<!--
90
+ The Reference tab: every shortcut and trick, in one place.
91
+
92
+ Keep this file in step with the code. When you add or change a shortcut, a
93
+ click target or a setting, change its line here in the same commit.
94
+ <kbd data-key="alt"> and <kbd data-key="command"> are filled in for the
95
+ user's keyboard (⌥ and ⌘ on a Mac, Alt and Ctrl elsewhere).
96
+ -->
97
+ <section class="block">
98
+ <div class="block-head"><h3>Inspect</h3></div>
99
+ <dl class="reference">
100
+ <dt><kbd data-key="alt"></kbd> hover</dt>
101
+ <dd>Box model of the element under the pointer, with a chip that names it.</dd>
102
+ <dt><kbd data-key="alt"></kbd> click</dt>
103
+ <dd>Pin the element. The panel opens on it.</dd>
104
+ <dt><kbd data-key="alt"></kbd> hover</dt>
105
+ <dd>With an element pinned: distances from it to the hovered element, in red.</dd>
106
+ <dt><kbd data-key="alt"></kbd> right-click</dt>
107
+ <dd>List every layer under the pointer, to reach one that sits behind another.</dd>
108
+ <dt>Inspect pill</dt>
109
+ <dd>Bottom right. Click it to lock inspect mode on, so none of this needs <kbd data-key="alt"></kbd>.</dd>
110
+ <dt><kbd>Esc</kbd></dt>
111
+ <dd>Turn the inspect lock off, and end a comparison.</dd>
112
+ </dl>
113
+ </section>
114
+
115
+ <section class="block">
116
+ <div class="block-head"><h3>Copy</h3></div>
117
+ <dl class="reference">
118
+ <dt>Click a value</dt>
119
+ <dd>Copies it. A size, a colour, a font weight: anything shown in monospace.</dd>
120
+ <dt>Click a token</dt>
121
+ <dd>Copies the token. Its theme path, its variable and its value in each theme copy separately.</dd>
122
+ <dt>Click “margin” or “padding”</dt>
123
+ <dd>In the box diagram: copies all four sides at once.</dd>
124
+ <dt>Click a component name</dt>
125
+ <dd>Copies the name. Each prop value copies on its own.</dd>
126
+ <dt>Click a class</dt>
127
+ <dd>Open the class list first; each chip copies its class.</dd>
128
+ </dl>
129
+ </section>
130
+
131
+ <section class="block">
132
+ <div class="block-head"><h3>Specs</h3></div>
133
+ <dl class="reference">
134
+ <dt>px · rem</dt>
135
+ <dd>Switch the unit lengths are shown and copied in.</dd>
136
+ <dt>Grid</dt>
137
+ <dd>Lay a 4 or 8 pixel grid over the element's board.</dd>
138
+ <dt>Compare</dt>
139
+ <dd>Makes the pinned element the baseline. Pin another to see only what differs.</dd>
140
+ <dt>Copy link</dt>
141
+ <dd>A link that reopens this page with the element pinned. Paste it in a ticket.</dd>
142
+ <dt>Pick colour</dt>
143
+ <dd>Pick any pixel on screen and see which token has that colour.</dd>
144
+ <dt>Breadcrumb</dt>
145
+ <dd>Click the parent's name to pin the parent.</dd>
146
+ <dt>fixed · hug · fill</dt>
147
+ <dd>Beside the size: whether each axis has a set size, follows its content or fills its parent.</dd>
148
+ <dt>No token</dt>
149
+ <dd>A colour written by hand that no token has. Ask the designer before you hard-code it.</dd>
150
+ </dl>
151
+ </section>
152
+
153
+ <section class="block">
154
+ <div class="block-head"><h3>All CSS</h3></div>
155
+ <dl class="reference">
156
+ <dt>Type over a value</dt>
157
+ <dd>Changes it on the page. <kbd>Enter</kbd> or clicking away saves; <kbd>Esc</kbd> cancels.</dd>
158
+ <dt><kbd>↑</kbd> <kbd>↓</kbd></dt>
159
+ <dd>Change a number by 1. With <kbd>Shift</kbd> by 10, with <kbd data-key="alt"></kbd> by 0.1.</dd>
160
+ <dt>↩ on a row</dt>
161
+ <dd>Removes your edit and puts back the page's own value. Clearing the value does the same.</dd>
162
+ <dt><kbd data-key="command"></kbd> <kbd>Enter</kbd></dt>
163
+ <dd>In the content box at the top: apply the new text.</dd>
164
+ <dt>Search</dt>
165
+ <dd>Filters properties by name. “Hide defaults” off shows every property.</dd>
166
+ <dt>Your edits</dt>
167
+ <dd>Are saved in this browser for this page and come back on reload. They never change the file.</dd>
168
+ </dl>
169
+ </section>
170
+
171
+ <section class="block">
172
+ <div class="block-head"><h3>Layers and panel</h3></div>
173
+ <dl class="reference">
174
+ <dt>Hover a layer</dt>
175
+ <dd>Outlines it on the page. Click to pin it; the arrow opens its children.</dd>
176
+ <dt>Drag the left edge</dt>
177
+ <dd>Resizes the panel. The width is remembered.</dd>
178
+ <dt>» menu</dt>
179
+ <dd>This reference, and “Collapse panel”. Click the collapsed rail to bring the panel back.</dd>
180
+ <dt>Changes tab</dt>
181
+ <dd>On a Claude Design export: pick an older export to list the boards that changed.</dd>
182
+ </dl>
183
+ </section>
184
+
185
+ <section class="block">
186
+ <div class="block-head"><h3>Good to know</h3></div>
187
+ <dl class="reference">
188
+ <dt>Sizes and distances</dt>
189
+ <dd>Are in the design's own pixels, whatever the zoom of the board.</dd>
190
+ <dt>Boards that look empty</dt>
191
+ <dd>Some exports draw a board only when it scrolls into view. Scroll to it first.</dd>
192
+ <dt>Props</dt>
193
+ <dd>Are listed as the design passes them. Check them against your own component before you use them.</dd>
194
+ </dl>
195
+ </section>
196
+ `,Pn={alt:ht,command:gt},Fn={mount(e){let t=V(e,`reference`);t.innerHTML=Nn;for(let e of t.querySelectorAll(`kbd[data-key]`))e.textContent=Pn[e.dataset.key??``]??``},render(){}};function In(e){let t=n=>{for(let r of n)`cssRules`in r&&t(r.cssRules),r instanceof CSSStyleRule&&e(r)};for(let e of document.styleSheets)try{t(e.cssRules)}catch{}}function Ln(e){let t=[];return In(n=>{try{e.matches(n.selectorText)&&t.push(n.style)}catch{}}),t.push(e.style),t}function Rn(){let e=new Map;return t=>{let n=e.get(t);return n||e.set(t,n=Ln(t)),n}}function zn(e,t){let n=e.findLast(e=>e.getPropertyValue(t));return n?n.getPropertyValue(t).trim():``}var Bn={linear:[0,0,1,1],ease:[.25,.1,.25,1],"ease-in":[.42,0,1,1],"ease-out":[0,0,.58,1],"ease-in-out":[.42,0,.58,1]};function Vn(e){let t=/^cubic-bezier\((.+)\)$/.exec(e)?.[1].split(`,`).map(Number);return t?.length===4?t:Bn[e]??Bn.linear}var Hn=e=>e.split(/,\s*(?![^(]*\))/);function Un(e,t,n){let r=Hn(t),i=Hn(n);return Hn(e).map((e,t)=>{let n=i[t%i.length];return{property:e,duration:r[t%r.length],easing:n,curve:Vn(n)}}).filter(e=>/[1-9]/.test(e.duration))}function Wn(e){let t=Un(e.transitionProperty,e.transitionDuration,e.transitionTimingFunction),n=e.animationName===`none`?[]:Un(e.animationName,e.animationDuration,e.animationTimingFunction);return[...t,...n]}function Gn(e,t,n){let r=e.parentElement?getComputedStyle(e.parentElement):null,i=r!==null&&r.display.includes(`flex`),a=i&&r.flexDirection.startsWith(`row`),o=t.flexGrow!==`0`,s=r&&t.alignSelf===`auto`?r.alignItems:t.alignSelf,c=s===`normal`||s===`stretch`,l=(e,t,r)=>{let a=zn(n,e);return a===`100%`?`fill`:a&&a!==`auto`&&!a.includes(`content`)?`fixed`:i?(t?o:c)?`fill`:`hug`:r?`fill`:`hug`};return`${l(`width`,a,[`block`,`flex`,`grid`,`list-item`,`table`].includes(t.display))} × ${l(`height`,i&&!a,!1)}`}function Kn([e,t,n,r]){return e===t&&e===n&&e===r?e:e===n&&t===r?`${e} ${t}`:`${e} ${t} ${n} ${r}`}function qn(e,t,n){return e.replace(/(-?\d*\.?\d+)px/g,(e,r)=>t===`px`?r:`${Math.round(Number(r)/n*1e3)/1e3}rem`)}function Jn(e,t,n,r){return t===`rem`?qn(e,`rem`,n):r===`sx`?qn(e,`px`,n):e}function Yn(e,t,n,r,i){return i===`sx`&&n===`px`?Xn(e,t):`${e}: ${Kn(t.map(e=>Jn(`${e}px`,n,r,`css`)))}`}function Xn(e,t){let[n,r,i,a]=t,o=e[0];return n===r&&n===i&&n===a?`${o}: ${n}`:n===i&&r===a?`${o}y: ${n}, ${o}x: ${r}`:`${o}t: ${n}, ${o}r: ${r}, ${o}b: ${i}, ${o}l: ${a}`}var Zn=`rgba(0, 0, 0, 0)`,q=(e,...t)=>t.includes(e)?``:e,Qn=e=>[e,...w.map(t=>`${e}-${t}`)];function $n(e,t){let n=T(e,t);return Kn(w.map(e=>`${n[e]}px`))}function er(e,t,n){let r=e.getBoundingClientRect(),i=ge(e,r),a=D(e,r),o=t.display.includes(`flex`),s=t.display.includes(`grid`),c=[{name:`size`,value:`${i.width} × ${i.height}`,props:[`width`,`height`],note:Gn(e,t,n(e))},{name:`on board`,value:a?.text??``,note:a?.board},{name:`padding`,value:q($n(t,`padding`),`0px`),props:Qn(`padding`)},{name:`margin`,value:q($n(t,`margin`),`0px`),props:Qn(`margin`)}];if(o){let e=t.flexWrap===`nowrap`?``:` wrap`;c.push({name:`layout`,value:`flex ${t.flexDirection}${e}`},{name:`align`,value:q(t.alignItems,`normal`)},{name:`justify`,value:q(t.justifyContent,`normal`)})}if(s&&c.push({name:`layout`,value:`grid`},{name:`columns`,value:q(t.gridTemplateColumns,`none`)}),(o||s)&&c.push({name:`gap`,value:q(t.gap,`normal`,`0px`),props:[`gap`,`row-gap`,`column-gap`]}),t.position!==`static`){let e=w.filter(e=>t.getPropertyValue(e)!==`auto`).map(e=>`${e} ${t.getPropertyValue(e)}`);c.push({name:`position`,value:[t.position,...e].join(` · `)})}return c}function tr(e){let t=$n(e,`border`),n=t!==`0px`,r=Kn([e.borderTopLeftRadius,e.borderTopRightRadius,e.borderBottomRightRadius,e.borderBottomLeftRadius]);return[{name:`background`,value:q(e.backgroundColor,Zn),color:!0,props:[`background-color`,`background`]},{name:`gradient`,value:q(e.backgroundImage,`none`),props:[`background-image`,`background`]},{name:`border`,value:n?`${t} ${e.borderTopStyle}`:``,props:[`border-width`,`border`,...Qn(`border`)]},{name:`border color`,value:n?e.borderTopColor:``,color:!0,props:[`border-color`,`border`,...Qn(`border`),`border-top-color`]},{name:`radius`,value:q(r,`0px`),props:[`border-radius`]},{name:`shadow`,value:q(e.boxShadow,`none`),props:[`box-shadow`]},{name:`opacity`,value:q(e.opacity,`1`),props:[`opacity`]}]}function nr(e,t){return g(e)||Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.nodeValue?.trim())?[{name:`font`,value:`${t.fontFamily.split(`,`)[0].replaceAll(`"`,``)} ${t.fontWeight} ${t.fontSize}/${t.lineHeight}`,props:[`font`,`font-family`,`font-weight`,`font-size`,`line-height`]},{name:`color`,value:t.color,color:!0,props:[`color`]},{name:`spacing`,value:q(t.letterSpacing,`normal`,`0px`),props:[`letter-spacing`]},{name:`transform`,value:q(t.textTransform,`none`)},{name:`align`,value:q(t.textAlign,`start`,`left`)},{name:`decoration`,value:q(t.textDecorationLine,`none`)}]:[]}function rr(e,t){return e instanceof SVGElement?[{name:`fill`,value:q(t.fill,`none`),color:!0,props:[`fill`,`color`]},{name:`stroke`,value:q(t.stroke,`none`),color:!0,props:[`stroke`,`color`]},{name:`stroke width`,value:t.stroke===`none`?``:t.strokeWidth,props:[`stroke-width`]}]:[]}function ir(e){return Wn(e).map(e=>({name:e.property,value:`${e.duration} ${e.easing}`}))}function ar(e,t,n){return[{label:`Layout`,rows:er(e,t,n)},{label:`Shape`,rows:tr(t)},{label:`Text`,rows:nr(e,t)},{label:`Icon`,rows:rr(e,t)},{label:`Motion`,rows:ir(t)}]}function or(e,t){let n=e=>new Map(e.flatMap(e=>e.rows.map(t=>[`${e.label}/${t.name}`,t]))),r=n(e),i=n(t),a=[...new Set([...r.keys(),...i.keys()])].filter(e=>!e.endsWith(`/on board`)).filter(e=>r.get(e)?.value||i.get(e)?.value),o=a.filter(e=>(r.get(e)?.value??``)!==(i.get(e)?.value??``)).map(e=>({name:e.split(`/`)[1],before:r.get(e),after:i.get(e)}));return{differences:o,identical:a.length-o.length}}var sr=new Set([`color`,`fill`,`stroke`,`font`,`font-family`,`font-size`,`font-weight`,`line-height`,`letter-spacing`]);function cr(e,t,n){let r=t.length>0&&t.every(e=>sr.has(e));for(let i=e;i;i=r?i.parentElement:null){let e=n(i),r=t.map(t=>e.findLast(e=>e.getPropertyValue(t))?.getPropertyValue(t)).filter(e=>e!==void 0);if(!r.length)continue;let a=r.flatMap(e=>[...e.matchAll(/var\(\s*(--[\w-]+)/g)]);return[...new Set(a.map(e=>e[1]))]}return[]}function lr(e,t){let n=t.find(t=>e.startsWith(t));return n?e.slice(n.length).replaceAll(`-`,`.`):``}var ur=new Map;function dr(e){let t=ur.get(e);if(!t){let n=new Map;In(t=>{let r=t.style.getPropertyValue(e).trim();r&&n.set(t.selectorText,r)}),t=new Set(n.values()).size>1?[...n]:[],ur.set(e,t)}return t}var fr=e=>Array.from(e).some(e=>e.startsWith(`--`)),pr=1200;function J(e,t,n){return e.classList.add(`copy`),e.title||=`Click to copy`,e.addEventListener(`click`,async r=>{r.stopPropagation();let i=`is-copied`;try{await navigator.clipboard.writeText(typeof t==`function`?t():t)}catch{i=`is-copy-blocked`}let a=n?[e,n]:[e];a.forEach(e=>e.classList.add(i)),setTimeout(()=>a.forEach(e=>e.classList.remove(i)),pr)}),e}var Y,X,mr=!1,hr=()=>parseFloat(getComputedStyle(document.documentElement).fontSize)||16,gr=e=>qn(e,N.unit,hr()),_r=e=>Jn(e,N.unit,hr(),l.copyFormat),vr=e=>Math.round(e*100)/100,yr=e=>e.color?an(e.value):gr(e.value);function br(e,t=!1){let n=v(`span`,t?`swatch is-small`:`swatch`);return n.style.setProperty(`--swatch`,e),n}function Z(e,t=``,n=``){let r=X.appendChild(v(`section`,`block ${n}`)),i=r.appendChild(v(`div`,`block-head`));return i.appendChild(v(`h3`,``,e)),t&&i.appendChild(v(`span`,`note`,t)),r}function xr(e,t,n=!1){let r=v(`div`,n?`wide`:`cell`);return r.appendChild(v(`span`,`label`,e)),J(r.appendChild(v(`span`,`value`,gr(t))),_r(t)),r}var Q=({sections:e},t,n)=>e.find(e=>e.label===t)?.rows.find(e=>e.name===n&&e.value);function Sr(e){let t=/(?:^|-)([A-Za-z]{6})$/.exec(e)?.[1]??``,n=/[a-z]/.test(t)&&/[A-Z]/.test(t);return/^(sc|css|jss)-/.test(e)||n}function Cr(e){let t=e.getBoundingClientRect(),n=D(e,t)?.board,r=e.parentElement,i=Array.from(e.classList),o=i.filter(e=>!Sr(e));Y.replaceChildren();let s=Y.appendChild(v(`div`,`crumbs`)),c=()=>s.appendChild(v(`span`,`sep`,`›`)).setAttribute(`aria-hidden`,`true`);if(n&&(s.appendChild(v(`span`,`crumb`,n)),c()),r&&r!==document.body){let e=s.appendChild(v(`button`,`crumb`,k(r)));e.title=`Pin the parent`,e.addEventListener(`click`,()=>a(r)),c()}s.appendChild(v(`span`,`crumb is-current`,k(e)));let l=Y.appendChild(v(`div`,`title-row`));l.appendChild(v(`span`,`tag-chip`,e.localName));let u=k(e);if(l.appendChild(v(`span`,`title`,u===e.localName?o[0]??u:u)),l.appendChild(v(`span`,`spacer`)),l.appendChild(v(`span`,`title-size`,A(e,t))),!i.length)return;let d=v(`button`,`more`);if(d.addEventListener(`click`,()=>{mr=!mr,Cr(e)}),mr){let e=Y.appendChild(v(`div`,`class-list`));[...o,...i.filter(Sr)].forEach(t=>{let n=v(`span`,`class-chip`,`.`+t);n.classList.toggle(`is-generated`,Sr(t)),e.appendChild(J(n,t))}),d.append(`Less`,x(`chevronUp`,10)),e.appendChild(d)}else{let e=Y.appendChild(v(`div`,`class-row`)),t=o.length?o:i;e.appendChild(v(`span`,`classes`,t.map(e=>`.`+e).join(` `)));let n=i.length-t.length;(n||i.length>2)&&(d.append(n?`+${n}`:`All`,x(`chevronDown`,10)),e.appendChild(d))}}function wr({el:e}){let t=$e(e);if(!t)return;let n=Z(t.inside?`Inside component`:`Component`,t.inside?`Contains this element`:`This element`,`component`),r=v(`div`,t.inside?`code is-inside`:`code`);r.appendChild(v(`span`,`name`,t.name));let i=r.appendChild(v(`span`,`copy-mark`));i.append(x(`copy`),x(`check`)),i.firstElementChild.classList.add(`when-idle`),i.lastElementChild.classList.add(`when-copied`),n.appendChild(J(r,t.name));let o=et(t);if(o.length){n.appendChild(v(`div`,`hint`,`Props in this design`));let e=n.appendChild(v(`div`,`props`));for(let t of o)e.appendChild(v(`span`,`prop-name`,t.name)),e.appendChild(J(v(`span`,`value`,t.value),t.value))}if(t.sourcePath){let e=t.sourcePath.split(`/`),r=e.pop()??``,i=n.appendChild(v(`div`,`source`));i.appendChild(v(`span`,`label`,`Source`));let a=i.appendChild(v(`span`,`path`));e.forEach(e=>a.append(e+`/`,document.createElement(`wbr`))),a.appendChild(v(`span`,`file`,r)),J(a,t.sourcePath)}let s=t.element;if(t.inside&&s){let t=n.appendChild(v(`div`,`select-row`)),r=t.appendChild(v(`button`,`ghost-button`));r.append(x(`chevronUp`,12),`Select component`),r.addEventListener(`click`,()=>a(s)),t.appendChild(v(`span`,`hint`,`Pinned: ${e.localName} inside it`))}}function Tr(){N.compareWith=null,s()}function Er(e,t){let n=v(`span`,`side ${t}`);return e?.value?(e.color&&n.appendChild(br(e.value,!0)),n.append(yr(e))):n.textContent=`none`,n}function Dr(e){let t=N.compareWith;if(!t?.isConnected)return;let n=Z(`Compare`),r=n.firstElementChild.appendChild(v(`button`,`ghost-button`));r.append(x(`close`,12),`Stop`,v(`kbd`,``,`Esc`)),r.addEventListener(`click`,Tr);let i=(e,t,r)=>{let i=n.appendChild(v(`div`,`who`));i.appendChild(v(`span`,e===`A`?`letter`:`letter is-b`,e)),i.appendChild(v(`span`,`name`,k(t))),i.appendChild(v(`span`,`board`,D(t,t.getBoundingClientRect())?.board??``)),i.appendChild(v(`span`,`spacer`)),i.appendChild(v(`span`,`hint`,r))};if(i(`A`,t,`Baseline`),t===e.el){n.appendChild(v(`div`,`compare-summary`,`Pin another element to compare it with A.`));return}i(`B`,e.el,`Pinned`);let{differences:a,identical:o}=or(ar(t,getComputedStyle(t),e.stylesOf),e.sections),s=a.length===1?`1 difference`:`${a.length} differences`;n.appendChild(v(`div`,`compare-summary`,`${s} · ${o} identical`));for(let e of a){let t=n.appendChild(v(`div`,`compare-row`));t.appendChild(v(`span`,`label`,e.name)),t.append(Er(e.before,`is-before`),x(`arrow`),Er(e.after,`is-after`))}}function Or(e,t){let n=v(`div`,`size-row`);n.appendChild(v(`span`,`label`,`Size`));let r=A(e);J(n.appendChild(v(`span`,`value`,r)),r),n.appendChild(v(`span`,`spacer`));let[i,a]=t.split(` × `),o=n.appendChild(v(`span`,`intents`)),s=(e,t)=>{o.appendChild(v(`span`,`intent ${t}`)).append(xe(e),e)};return s(i,`is-width`),o.appendChild(v(`span`,``,`×`)),s(a,`is-height`),n}function kr(e,t){return t.flatMap(([t,n])=>cr(e.el,n?.props??[],e.stylesOf).map(e=>{let n=v(`div`,`wide`);n.appendChild(v(`span`,`label`,t));let r=lr(e,l.tokenPrefixes);return J(n.appendChild(v(`span`,`token-note`,r||e)),r||e),n}))}function Ar(e){let{el:t,cs:n}=e,r=D(t,t.getBoundingClientRect()),i=Z(`Layout`,r?`X, Y on board`:``);i.appendChild(Or(t,Q(e,`Layout`,`size`)?.note??`hug × hug`));let a=(t,n)=>Q(e,t,n),o=n.display.includes(`flex`),s=[[`X`,r?String(r.x):``],[`Y`,r?String(r.y):``],[`Display`,n.display],[`Direction`,o?n.flexDirection+(n.flexWrap===`nowrap`?``:` wrap`):``],[`Align`,a(`Layout`,`align`)?.value],[`Justify`,a(`Layout`,`justify`)?.value],[`Gap`,a(`Layout`,`gap`)?.value,a(`Layout`,`gap`)],[`Radius`,a(`Shape`,`radius`)?.value,a(`Shape`,`radius`)],[`Border`,a(`Shape`,`border`)?.value,a(`Shape`,`border`)],[`Opacity`,a(`Shape`,`opacity`)?.value,a(`Shape`,`opacity`)],[`Stroke`,a(`Icon`,`stroke width`)?.value,a(`Icon`,`stroke width`)]],c=i.appendChild(v(`div`,`grid2`));s.forEach(([e,t])=>t&&c.appendChild(xr(e,t)));let l=[[`Position`,a(`Layout`,`position`)],[`Columns`,a(`Layout`,`columns`)],[`Shadow`,a(`Shape`,`shadow`)],[`Gradient`,a(`Shape`,`gradient`)]];l.forEach(([e,t])=>t&&i.appendChild(xr(e,t.value,!0)));let u=[...s.map(([e,,t])=>[e,t]),...l];i.append(...kr(e,u))}function jr(e,t){for(let n of w){let r=`${vr(t[n])}px`;e.appendChild(J(v(`span`,`box-side is-${n}`,gr(r)),_r(r)))}}function Mr({el:e,cs:t}){let n=T(t,`margin`),r=T(t,`padding`),i=T(t,`border`),a=ge(e,e.getBoundingClientRect()),o=a.width-r.left-r.right-i.left-i.right,s=a.height-r.top-r.bottom-i.top-i.bottom,c=(e,t)=>Yn(e,w.map(e=>vr(t[e])),N.unit,hr(),l.copyFormat),u=Z(`Box`).appendChild(v(`div`,`box-margin`)),d=J(v(`span`,`box-name`,`margin`),()=>c(`margin`,n));d.title=`Click to copy all four sides`,u.appendChild(d),jr(u,n);let f=u.appendChild(v(`div`,`box-padding`)),p=J(v(`span`,`box-name`,`padding`),()=>c(`padding`,r));p.title=`Click to copy all four sides`,f.appendChild(p),jr(f,r);let m=`${vr(o)} × ${vr(s)}`;f.appendChild(v(`div`,`box-content`,m))}function Nr(e){let t=/([\w-]+)["'\]]*\s*$/.exec(e)?.[1]??e;return t===`root`?`default`:t}function Pr(e,t,n){let{el:r,cs:i,stylesOf:a}=n,o=v(`div`,`token-row`),s=(e,t)=>J(e,t,o),[c,u=`100%`]=an(e.value).split(` `),d=o.appendChild(v(`div`,`token-line`));d.appendChild(br(e.value)),d.appendChild(s(v(`span`,`value`,c),c)),on(c)&&d.appendChild(v(`span`,`alpha`,u));let f=cr(r,e.props??[],a),p=f.length||!fr(i)?[]:rn(i,e.value),m=!f.length&&!p.length&&fr(i);p.length&&d.appendChild(v(`span`,`badge`,`Matches token`)),m&&(d.appendChild(v(`span`,`badge is-warn`)).append(x(`warning`,10),`No token`),o.classList.add(`is-missing`)),d.appendChild(v(`span`,`spacer`));let h=d.appendChild(v(`span`,`role`));h.appendChild(v(`span`,`name`,t)),h.appendChild(v(`span`,`copy-hint`)).append(x(`copy`,12),`Copy`),h.appendChild(v(`span`,`copied`)).append(x(`check`,12),`Copied`);for(let e of f){let t=lr(e,l.tokenPrefixes);t&&o.appendChild(s(v(`div`,`theme-path`,t),t)),o.appendChild(s(v(`div`,t?`css-var is-secondary`:`css-var`,e),e));let n=dr(e);n.length&&o.appendChild(Fr(n,i.getPropertyValue(e).trim()))}return p.slice(0,2).forEach(e=>{o.appendChild(s(v(`div`,`css-var is-secondary`,e),e))}),m&&o.appendChild(v(`div`,`warn-text`,`Hard-coded value. No token has this colour.`)),o}function Fr(e,t){let n=v(`div`,`modes`);for(let[r,i]of e){let e=n.appendChild(v(`span`,i===t?`mode is-active`:`mode`));on(i)&&e.appendChild(br(i,!0)),e.append(i,v(`span`,`scope`,Nr(r))),J(e,i)}return n}var Ir=[[`Shape`,`background`,`Background`],[`Shape`,`border color`,`Border`],[`Text`,`color`,`Text`],[`Icon`,`fill`,`Fill`],[`Icon`,`stroke`,`Stroke`]];function Lr(e){let t=Ir.flatMap(([t,n,r])=>{let i=Q(e,t,n);return i?[Pr(i,r,e)]:[]});N.pickedColor&&t.push(Pr({name:`picked`,value:N.pickedColor,color:!0},`Picked`,e)),t.length&&Z(`Colors`).appendChild(v(`div`,`token-rows`)).append(...t)}function Rr(e){let{cs:t}=e,n=Q(e,`Text`,`font`);if(!n)return;let r=Z(`Typography`),i=t.fontFamily.split(`,`)[0].replaceAll(`"`,``);r.appendChild(xr(`Family`,i));let a=t=>Q(e,`Text`,t)?.value,o=[[`Weight`,t.fontWeight],[`Size`,t.fontSize],[`Line`,t.lineHeight],[`Tracking`,a(`spacing`)],[`Transform`,a(`transform`)],[`Align`,a(`align`)],[`Decoration`,a(`decoration`)]],s=r.appendChild(v(`div`,`grid2`));o.forEach(([e,t])=>t&&s.appendChild(xr(e,t))),r.append(...kr(e,[[`Font`,n]]))}function zr({cs:e}){let t=Wn(e);if(!t.length)return;let n=Z(`Motion`);for(let e of t){let t=n.appendChild(v(`div`,`motion-row`));t.appendChild(Se(e.curve)),t.appendChild(v(`span`,`property`,e.property)),t.appendChild(v(`span`,`duration`,e.duration)),t.appendChild(v(`span`,`easing`,e.easing)),t.title=`Click to copy`,J(t,`${e.property} ${e.duration} ${e.easing}`)}}function Br(e,t,n){let r=Array.from(e.querySelectorAll(`.segment`)),i=()=>{r.forEach(e=>{e.setAttribute(`aria-checked`,String(e.dataset.value===t()))}),e.classList.toggle(`is-on`,e.querySelector(`.is-on[aria-checked="true"]`)!==null)};r.forEach(e=>{e.addEventListener(`click`,()=>{n(e.dataset.value??``),i(),s()})}),i()}function Vr(e){Br(B(e,`[data-option="unit"]`),()=>N.unit,t=>{N.unit=t===`rem`?`rem`:`px`,An(e)}),Br(B(e,`[data-option="grid"]`),()=>String(N.gridStep),e=>{N.gridStep=Number(e)});let t=B(e,`[data-action="compare"]`),r=()=>t.setAttribute(`aria-pressed`,String(N.compareWith!==null));t.addEventListener(`click`,()=>{N.compareWith=N.compareWith?null:n(),s()}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&N.compareWith&&Tr()});let i=B(e,`[data-action="pick"]`);i.hidden=!window.EyeDropper,i.addEventListener(`click`,async()=>{i.setAttribute(`aria-pressed`,`true`);try{N.pickedColor=(await new window.EyeDropper().open()).sRGBHex}catch{}i.setAttribute(`aria-pressed`,`false`),s()}),J(B(e,`[data-action="link"]`),()=>{let e=n();return e?bt(e):location.href}),Hr=r}var Hr=()=>{};function Ur(e){let t=getComputedStyle(e),n=Rn(),r={el:e,cs:t,stylesOf:n,sections:ar(e,t,n)};Hr(),Cr(e);let i=X.scrollTop;X.replaceChildren(),Dr(r),wr(r),Ar(r),Mr(r),Lr(r),Rr(r),zr(r),X.scrollTop=i}var Wr=[{mount(e){Y=B(e,`.element-header`),X=V(e,`specs`),Vr(e)},render(e,t){t===`pinned`&&(X.scrollTop=0),Ur(e)}},bn,En,en,Fn],Gr=320,Kr=560,$=null;function qr(){De(`--ins-dock`,`${$!==null&&$.style.display!==`none`?$.offsetWidth:0}px`)}function Jr(e,t){e.classList.toggle(`is-collapsed`,t),qr()}function Yr(e){B(e,`.resize`).addEventListener(`mousedown`,t=>{t.preventDefault();let n=e.offsetWidth,r=r=>{let i=n+t.clientX-r.clientX;e.style.width=`${Math.max(Gr,Math.min(Kr,i))}px`,qr()},i=()=>{document.removeEventListener(`mousemove`,r),document.removeEventListener(`mouseup`,i),An(e)};document.addEventListener(`mousemove`,r),document.addEventListener(`mouseup`,i)})}function Xr(e){let t=B(e,`.more-button`),n=B(e,`.more-menu`),r=e=>{n.hidden=!e,t.setAttribute(`aria-expanded`,String(e))};t.addEventListener(`click`,e=>{e.stopPropagation(),r(n.hidden)}),e.addEventListener(`click`,()=>r(!1)),e.addEventListener(`keydown`,e=>{e.key===`Escape`&&r(!1)}),B(n,`[data-tab="reference"]`).addEventListener(`click`,()=>jn(e,`reference`)),B(n,`.collapse`).addEventListener(`click`,()=>Jr(e,!0))}function Zr(){let e=S(v(`div`,`panel`));return e.innerHTML=Mn,kn(e),e.addEventListener(`keydown`,e=>e.stopPropagation()),B(e,`.tabs`).addEventListener(`click`,t=>{let n=t.target.closest(`.tab`)?.dataset.tab;n&&jn(e,n)}),B(e,`.close`).addEventListener(`click`,o),Xr(e),B(e,`.rail`).addEventListener(`click`,()=>Jr(e,!1)),Yr(e),Wr.forEach(t=>t.mount(e)),e}function Qr(){r((e,t)=>{e?($??=Zr(),$.style.display=``,B($,`.rail .name`).textContent=`Inspect · ${k(e)}`,t===`pinned`&&Jr($,!1),Wr.forEach(n=>n.render(e,t)),qr()):($&&($.style.display=`none`),qr())})}function $r(){d(),Mt(),Qr(),pt(),Ft(),_t(),_(),document.addEventListener(`DOMContentLoaded`,()=>_()),window.addEventListener(`load`,()=>_()),xt(),window.Groma={pin:a,pinnedElement:n,replayOverrides:_,highlight:Ot,configure:u}}window.Groma||$r()})();
197
+ //# sourceMappingURL=groma.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groma.js","names":["styles","body","body","body","reference","template","pinned","listeners","pinnedElement","onSelection","listener","notify","change","pin","el","unpin","rerender","DEFAULTS","config","configure","changes","prefix","configureFromScriptTag","tag","readStore","key","writeStore","value","STYLE_KEY","CONTENT_KEY","originalStyles","originalContent","inlineStyle","savedStyles","selector","saveStyle","all","props","prop","applyStyle","style","originals","restoreStyle","priority","VALUE_TAGS","hasValue","getContent","setContent","content","hasSavedContent","saveContent","applyContent","restoreContent","original","forEachMatch","root","apply","replayOverrides","h","className","text","placeOver","box","scaleOf","offsetWidth","rect","offsetHeight","visibleBox","left","top","right","bottom","parent","overflowX","overflowY","clip","layoutSize","rounded","SVG_NS","ICONS","INTENT_GLYPHS","svg","size","grid","drawing","icon","name","intentGlyph","intent","easingGlyph","x1","y1","x2","y2","x","t","y","styles_default","HOST_TAG","host","inspectorHost","mount","node","setRootVariable","isOwn","whenBodyExists","start","SIDES","sidesOf","suffix","property","side","cs","layer","overlay","addBox","kind","addLabel","label","transform","clearBoxes","drawRing","thickness","innerHeight","outer","rightEdge","bottomEdge","drawBoxModel","scale","onScreen","sides","margin","drawChildrenAndGaps","rects","child","childStyle","childRect","isRow","a","b","i","ROOM_FOR_LABEL","BESIDE","contains","inner","crossing","lo","lo1","lo2","hi","hi1","hi2","distanceText","pixels","drawDistances","insets","gaps","shortLabels","from","to","axis","length","middle","BOARD_SELECTOR","boardOf","boardPosition","board","boardRect","tenth","MAX_DEPTH","NOT_PROPS","customElement","depth","attribute","declaredProps","declaredComponent","designSystem","designSystemComponents","script","header","exported","namespace","components","entry","fiberOf","rootElement","fiber","READERS","known","direct","inside","type","ownName","displayName","usable","styledComponentId","componentOf","read","component","componentProps","elementName","sizeText","width","height","tagAndClasses","first","classes","GAP","EDGE","tooltip","showTooltip","position","above","maxLeft","hideTooltip","altHeld","locked","hovered","isInspecting","clearHover","update","setInspectLock","on","inspect","target","pinnedRect","startHover","setAlt","held","e","isApple","ALT_KEY","COMMAND_KEY","startLauncher","pill","hold","off","setLocked","getSelector","parts","id","sameTag","sibling","nth","PARAM","elementLink","url","pinFromLink","tries","timer","options","TAG_HEIGHT","pinnedOutline","pinTag","hoverOutline","outlined","frame","placeGrid","visible","origin","cell","tick","showGrid","startTicking","highlight","syncOutlines","showHoverOutline","hideHoverOutline","startOutlines","OFFSET","menu","closeMenu","openMenu","layers","pointerX","pointerY","list","item","startLayerMenu","EMBEDDED_DATA","readExport","data","json","parseExport","html","currentExport","lineCounts","counts","line","trimmed","extraLines","otherCounts","diffExports","file","titleOf","files","current","older","before","after","part","panel","tabBody","tab","MAX_LINES","MAX_LINE_LENGTH","head","body$3","fileInput","chooseFile","boardName","rest","title","fileButton","button","isError","fileName","groupHeading","count","changedRow","row","caret","totals","total","sign","lines","addLines","plainRow","renderEmpty","empty","choose","renderError","alert","renderChanges","diff","dates","changesTab","probe","normalized","normalizeColor","result","tokensWithColor","wanted","color","formatColor","hex","channel","alpha","isColorValue","CSS_GROUPS","DEFAULT_VALUES","body$2","focusAfterRender","elementRows","rectRows","variableRows","rowShell","rowEl","valueEl","readOnlyRow","editableRow","shown","overridden","nudged","commit","typed","next","number","step","decimals","reset","TEXT_TAGS","contentEditor","note","section","editor","footer","renderCss","scrollTop","saved","addSection","rows","labelClass","matchesSearch","editable","custom","group","computed","mountControls","search","setSearch","query","cssTab","NOT_LAYERS","body$1","layerChildren","treeNode","wrap","children","childList","isOpen","setOpen","open","renderLayers","layersTab","LAYOUT_KEY","TAB_IDS","loadLayout","layout","saveLayout","setTab","panel_default","reference_default","KEYS","referenceTab","body","forEachPageRule","walk","rule","rules","visit","sheet","appliedStyles","styles","createStyleLookup","cache","declaredValue","candidate","KEYWORD_CURVES","easingCurve","numbers","easing","listItems","zip","durationList","durations","easingList","easings","properties","motionItems","transitions","animations","sizingIntent","inFlex","rowParent","grows","alignment","stretches","declared","isMainAxis","fillsByDefault","compactSides","shownLength","unit","rootFontSize","copiedLength","format","copiedSpacing","sxSpacing","TRANSPARENT","unless","defaults","sideProps","sidesText","layoutRows","onBoard","isFlex","isGrid","stylesOf","shapeRows","borderWidths","hasBorder","radius","textRows","iconRows","motionRows","specSections","compareSpecs","flatten","sections","spec","base","other","keys","differences","INHERITED","declaredTokens","inherits","values","names","match","tokenPath","prefixes","token","modesByToken","tokenModes","modes","byScope","hasTokens","FEEDBACK_MS","copyOnClick","marked","feedbackOn","classesExpanded","copied","hundredth","shownSpec","swatch","chip","small","block","wide","findSpec","s","looksGenerated","hash","mixedCase","renderHeader","meaningful","crumbs","separator","up","toggle","hidden","renderComponent","mark","folders","source","path","folder","select","stopComparing","compareSide","renderCompare","baseline","stop","who","letter","role","context","identical","difference","sizeRow","intents","word","tokenNotes","specs","renderLayout","sectionName","cells","tokenised","boxSides","renderBox","padding","border","contentWidth","contentHeight","spacing","marginBox","marginName","paddingBox","paddingName","scopeName","colorRow","copies","similar","missing","slot","modesLine","scope","mode","COLOR_ROLES","renderColors","renderTypography","font","family","renderMotion","items","segmented","segments","show","segment","mountActionBar","compare","showCompare","pick","renderActionStates","renderSpecs","TABS","MIN_WIDTH","MAX_WIDTH","reportDock","setCollapsed","collapsed","resizeByDragging","down","startWidth","move","mountMoreMenu","createPanel","startPanel"],"sources":["../src/selection.ts","../src/config.ts","../src/edits/storage.ts","../src/edits/overrides.ts","../src/dom.ts","../src/icons.ts","../src/root.ts","../src/box-sides.ts","../src/overlay/layer.ts","../src/overlay/box-model.ts","../src/overlay/distances.ts","../src/board.ts","../src/specs/components/custom-element.ts","../src/specs/components/declared.ts","../src/specs/components/react.ts","../src/specs/component.ts","../src/element-name.ts","../src/overlay/tooltip.ts","../src/overlay/hover.ts","../src/platform.ts","../src/launcher.ts","../src/edits/selector.ts","../src/link.ts","../src/options.ts","../src/outlines.ts","../src/overlay/layer-menu.ts","../src/export-diff.ts","../src/panel/tab.ts","../src/panel/changes-tab.ts","../src/specs/colors.ts","../src/panel/css-properties.ts","../src/panel/css-tab.ts","../src/panel/layers-tab.ts","../src/panel/layout.ts","../src/panel/reference-tab.ts","../src/page-styles.ts","../src/specs/motion.ts","../src/specs/sizing.ts","../src/specs/units.ts","../src/specs/sections.ts","../src/specs/tokens.ts","../src/panel/copy.ts","../src/panel/specs-tab.ts","../src/panel/panel.ts","../src/main.ts"],"sourcesContent":["/**\n * The pinned element: the one the panel describes.\n *\n * Modules that pick an element (Alt+click, the Layers tree, a shared link)\n * call `pin`. Modules that show it (the panel tabs, the outline) subscribe\n * with `onSelection`, so neither side has to know the other.\n */\n\n/** \"pinned\": another element was chosen. \"updated\": same element, new data. */\nexport type SelectionChange = \"pinned\" | \"updated\";\n\ntype Listener = (el: Element | null, change: SelectionChange) => void;\n\nlet pinned: Element | null = null;\nconst listeners: Listener[] = [];\n\nexport const pinnedElement = (): Element | null => pinned;\n\nexport function onSelection(listener: Listener): void {\n listeners.push(listener);\n}\n\nfunction notify(change: SelectionChange): void {\n listeners.forEach((listener) => listener(pinned, change));\n}\n\nexport function pin(el: Element): void {\n pinned = el;\n notify(\"pinned\");\n}\n\nexport function unpin(): void {\n pinned = null;\n notify(\"pinned\");\n}\n\n/** Call after an edit or an option change so everything shows the new state. */\nexport function rerender(): void {\n notify(\"updated\");\n}\n","/**\n * What the page that hosts the inspector can set, so the inspector speaks\n * the project's language. Nothing here is required.\n *\n * Set it on the script tag:\n *\n * <script src=\"groma.js\" data-copy=\"sx\" data-token-prefix=\"--ds-\"></script>\n *\n * or from code: `Groma.configure({ copyFormat: \"sx\" })`.\n */\nimport { rerender } from \"./selection\";\n\nexport type CopyFormat = \"css\" | \"sx\";\n\nexport interface Config {\n /**\n * What a click on a length copies.\n * \"css\" `24px`, and `padding: 12px 16px` for a box side group\n * \"sx\" `24`, and `py: 12, px: 16`: the object form some styling\n * libraries take, where a bare number means pixels\n */\n copyFormat: CopyFormat;\n /**\n * Prefixes of CSS variables that mirror a theme object. A token with one\n * of these prefixes also shows its path in that object:\n * \"--ds-\" turns `--ds-color-text-primary` into `color.text.primary`.\n */\n tokenPrefixes: string[];\n}\n\nconst DEFAULTS: Config = { copyFormat: \"css\", tokenPrefixes: [] };\n\nexport const config: Config = { ...DEFAULTS };\n\n/** Changes some settings; the rest keep their values. `configure()` alone restores the defaults. */\nexport function configure(changes: Partial<Config> = DEFAULTS): void {\n if (changes.copyFormat === \"css\" || changes.copyFormat === \"sx\") {\n config.copyFormat = changes.copyFormat;\n }\n if (Array.isArray(changes.tokenPrefixes)) {\n config.tokenPrefixes = changes.tokenPrefixes.filter((prefix) => prefix.startsWith(\"--\"));\n }\n rerender();\n}\n\n/** Reads the settings from the script tag that loaded the inspector. */\nexport function configureFromScriptTag(): void {\n // A module script has no `currentScript`; such pages mark the tag instead.\n const tag = document.currentScript ?? document.querySelector(\"script[data-groma]\");\n if (!(tag instanceof HTMLElement)) return;\n configure({\n copyFormat: tag.dataset.copy as CopyFormat | undefined,\n tokenPrefixes: tag.dataset.tokenPrefix?.split(/[\\s,]+/).filter(Boolean),\n });\n}\n","/** JSON objects in localStorage that never throw. */\n\nexport function readStore<T extends object>(key: string): Partial<T> {\n try {\n return JSON.parse(localStorage.getItem(key) ?? \"{}\") ?? {};\n } catch {\n return {};\n }\n}\n\nexport function writeStore(key: string, value: object): void {\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // Storage is blocked or full: the change still applies, it is just not saved.\n }\n}\n","/**\n * Edits made in the All CSS tab: style overrides and content changes.\n *\n * They are saved in localStorage per page, keyed by element selector, and\n * replayed when the page loads. What the page had before the first edit is\n * remembered, so a reset can put it back.\n */\nimport { readStore, writeStore } from \"./storage\";\n\n// Scoped by page: every file:// page shares one localStorage.\nconst STYLE_KEY = \"__groma_overrides:\" + location.pathname;\nconst CONTENT_KEY = \"__groma_html:\" + location.pathname;\n\ntype StyleOverrides = Record<string, Record<string, string>>;\ntype ContentOverrides = Record<string, string>;\n\n/** Inline value and priority of a property before its first edit. */\nconst originalStyles = new WeakMap<Element, Record<string, [string, string]>>();\nconst originalContent = new WeakMap<Element, string>();\n\nconst inlineStyle = (el: Element): CSSStyleDeclaration => (el as HTMLElement).style;\n\n// ── styles ─────────────────────────────────────────────────────\n\nexport function savedStyles(selector: string): Record<string, string> {\n return readStore<StyleOverrides>(STYLE_KEY)[selector] ?? {};\n}\n\n/** Saves one property; `null` forgets it. */\nexport function saveStyle(selector: string, prop: string, value: string | null): void {\n const all = readStore<StyleOverrides>(STYLE_KEY);\n const props = (all[selector] ??= {});\n if (value === null) delete props[prop];\n else props[prop] = value;\n if (!Object.keys(props).length) delete all[selector];\n writeStore(STYLE_KEY, all);\n}\n\nexport function applyStyle(el: Element, prop: string, value: string): void {\n const style = inlineStyle(el);\n const originals = originalStyles.get(el) ?? {};\n originalStyles.set(el, originals);\n originals[prop] ??= [style.getPropertyValue(prop), style.getPropertyPriority(prop)];\n style.setProperty(prop, value);\n}\n\n/** Puts back the inline value the page had before the first edit. */\nexport function restoreStyle(el: Element, prop: string): void {\n const [value, priority] = originalStyles.get(el)?.[prop] ?? [\"\", \"\"];\n inlineStyle(el).setProperty(prop, value, priority);\n}\n\n// ── content ────────────────────────────────────────────────────\n\nconst VALUE_TAGS = new Set([\"input\", \"textarea\", \"select\", \"option\"]);\n\n/** Form controls are edited through their value, everything else through its HTML. */\nexport const hasValue = (el: Element): boolean => VALUE_TAGS.has(el.localName);\n\nexport function getContent(el: Element): string {\n return hasValue(el) ? (el as HTMLInputElement).value : el.innerHTML;\n}\n\nfunction setContent(el: Element, content: string): void {\n if (hasValue(el)) (el as HTMLInputElement).value = content;\n else el.innerHTML = content;\n}\n\nexport function hasSavedContent(selector: string): boolean {\n return selector in readStore<ContentOverrides>(CONTENT_KEY);\n}\n\n/** Saves an element's content; `null` forgets it. */\nexport function saveContent(selector: string, content: string | null): void {\n const all = readStore<ContentOverrides>(CONTENT_KEY);\n if (content === null) delete all[selector];\n else all[selector] = content;\n writeStore(CONTENT_KEY, all);\n}\n\nexport function applyContent(el: Element, content: string): void {\n if (!originalContent.has(el)) originalContent.set(el, getContent(el));\n setContent(el, content);\n}\n\n/** Puts back the content the page had before the first edit. */\nexport function restoreContent(el: Element): void {\n const original = originalContent.get(el);\n if (original !== undefined) setContent(el, original);\n}\n\n// ── replay ─────────────────────────────────────────────────────\n\nfunction forEachMatch(root: ParentNode, selector: string, apply: (el: Element) => void): void {\n try {\n root.querySelectorAll(selector).forEach(apply);\n } catch {\n // A selector saved by an older version that this browser rejects.\n }\n}\n\n/** Applies every saved edit to the page, or only inside `root`. */\nexport function replayOverrides(root: ParentNode = document): void {\n Object.entries(readStore<StyleOverrides>(STYLE_KEY)).forEach(([selector, props = {}]) => {\n forEachMatch(root, selector, (el) => {\n Object.entries(props).forEach(([prop, value]) => applyStyle(el, prop, value));\n });\n });\n Object.entries(readStore<ContentOverrides>(CONTENT_KEY)).forEach(([selector, content = \"\"]) => {\n forEachMatch(root, selector, (el) => applyContent(el, content));\n });\n}\n","/** DOM helpers shared by every module. */\n\n/** A rectangle in viewport pixels. A `DOMRect` is one. */\nexport interface Box {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\nexport interface Scale {\n x: number;\n y: number;\n}\n\n/** Creates an element with a class and, optionally, text. */\nexport function h<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className = \"\",\n text?: string,\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n if (className) el.className = className;\n if (text !== undefined) el.textContent = text;\n return el;\n}\n\n/** Moves and sizes a fixed-position node so it covers `box`. */\nexport function placeOver(node: HTMLElement, box: Box): void {\n node.style.left = `${box.left}px`;\n node.style.top = `${box.top}px`;\n node.style.width = `${box.width}px`;\n node.style.height = `${box.height}px`;\n}\n\n/**\n * How much CSS transforms (a zoomed artboard) scale an element on screen.\n * Computed sizes ignore transforms; the on-screen rectangle does not.\n */\nexport function scaleOf(el: Element, rect: Box): Scale {\n // SVG elements have no offset size; treat them as unscaled.\n const { offsetWidth, offsetHeight } = el as HTMLElement;\n return {\n x: offsetWidth ? rect.width / offsetWidth : 1,\n y: offsetHeight ? rect.height / offsetHeight : 1,\n };\n}\n\n/**\n * The part of an element's rectangle the page actually shows: what is left\n * after every scrolling or clipping ancestor has cut it.\n */\nexport function visibleBox(el: Element, rect: Box): Box {\n let left = rect.left;\n let top = rect.top;\n let right = rect.left + rect.width;\n let bottom = rect.top + rect.height;\n for (let parent = el.parentElement; parent; parent = parent.parentElement) {\n const { overflowX, overflowY } = getComputedStyle(parent);\n if (overflowX === \"visible\" && overflowY === \"visible\") continue;\n const clip = parent.getBoundingClientRect();\n left = Math.max(left, clip.left);\n top = Math.max(top, clip.top);\n right = Math.min(right, clip.right);\n bottom = Math.min(bottom, clip.bottom);\n }\n return { left, top, width: Math.max(0, right - left), height: Math.max(0, bottom - top) };\n}\n\n/** The element's border-box size in its own CSS pixels, at any zoom. */\nexport function layoutSize(el: Element, rect: Box): { width: number; height: number } {\n const { offsetWidth, offsetHeight } = el as HTMLElement;\n const rounded = (value: number) => Math.round(value * 100) / 100;\n return {\n width: offsetWidth ?? rounded(rect.width),\n height: offsetHeight ?? rounded(rect.height),\n };\n}\n\n/** A short name for an element: \"div#id.first-class.second-class\". */\nexport function elementLabel(el: Element, maxClasses = 3): string {\n const classes = Array.from(el.classList)\n .slice(0, maxClasses)\n .map((name) => \".\" + name)\n .join(\"\");\n return el.localName + (el.id ? \"#\" + el.id : \"\") + classes;\n}\n","/** The inspector's icons: stroke drawings on a 24px grid, sized where they are used. */\n\nconst SVG_NS = \"http://www.w3.org/2000/svg\";\n\n/** The drawing inside each icon's `<svg>`. */\nconst ICONS = {\n check: '<path d=\"M20 6 9 17l-5-5\"/>',\n arrow: '<path d=\"M5 12h14M13 6l6 6-6 6\"/>',\n close: '<path d=\"M18 6 6 18M6 6l12 12\"/>',\n chevronDown: '<path d=\"m6 9 6 6 6-6\"/>',\n chevronRight: '<path d=\"m9 18 6-6-6-6\"/>',\n chevronUp: '<path d=\"m18 15-6-6-6 6\"/>',\n copy: '<rect x=\"9\" y=\"9\" width=\"12\" height=\"12\" rx=\"2\"/><path d=\"M5 15V5a2 2 0 0 1 2-2h10\"/>',\n file:\n '<path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\"/>' +\n '<path d=\"M14 2v4a2 2 0 0 0 2 2h4\"/>',\n warning:\n '<path d=\"M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z\"/>' +\n '<path d=\"M12 9v4M12 17h.01\"/>',\n inspect: '<circle cx=\"12\" cy=\"12\" r=\"8\"/><path d=\"M12 2v4M12 18v4M2 12h4M18 12h4\"/>',\n} as const;\n\n/** Sizing-intent glyphs, drawn on a 12px grid. */\nconst INTENT_GLYPHS = {\n fixed: '<path d=\"M2 6h8M2 3.5v5M10 3.5v5\"/>',\n hug: '<path d=\"M1.5 3.5 4 6 1.5 8.5M10.5 3.5 8 6l2.5 2.5M6 3v6\"/>',\n fill: '<path d=\"M1.5 6h9M4 3.5 1.5 6 4 8.5M8 3.5 10.5 6 8 8.5\"/>',\n} as const;\n\nexport type IconName = keyof typeof ICONS;\nexport type Intent = keyof typeof INTENT_GLYPHS;\n\nfunction svg(drawing: string, size: number, grid: number): SVGSVGElement {\n const el = document.createElementNS(SVG_NS, \"svg\");\n el.setAttribute(\"class\", \"icon\");\n el.setAttribute(\"width\", String(size));\n el.setAttribute(\"height\", String(size));\n el.setAttribute(\"viewBox\", `0 0 ${grid} ${grid}`);\n el.innerHTML = drawing;\n return el;\n}\n\nexport const icon = (name: IconName, size = 14): SVGSVGElement => svg(ICONS[name], size, 24);\n\nexport const intentGlyph = (intent: Intent): SVGSVGElement => svg(INTENT_GLYPHS[intent], 12, 12);\n\n/**\n * A 16px easing curve with its axes, for a CSS timing function given as its\n * four cubic-bezier numbers.\n */\nexport function easingGlyph([x1, y1, x2, y2]: number[]): SVGSVGElement {\n const x = (t: number) => 2 + 12 * t;\n const y = (t: number) => 14 - 12 * t;\n const curve = `M2 14C${x(x1)} ${y(y1)} ${x(x2)} ${y(y2)} 14 2`;\n return svg(`<path d=\"M2 2v12h12\" opacity=\"0.4\"/><path d=\"${curve}\"/>`, 16, 16);\n}\n","/**\n * The inspector's own corner of the page.\n *\n * Every node the inspector draws lives in one shadow root, so the page's CSS\n * cannot restyle the inspector and the inspector's CSS cannot leak out.\n */\nimport { h } from \"./dom\";\nimport styles from \"./styles/index.css?inline\";\n\nconst HOST_TAG = \"groma-inspector\";\n\nlet host: HTMLElement | null = null;\n\nfunction inspectorHost(): HTMLElement {\n if (!host) {\n host = document.body.appendChild(document.createElement(HOST_TAG));\n host.attachShadow({ mode: \"open\" }).appendChild(h(\"style\", \"\", styles));\n }\n return host;\n}\n\n/** Adds a top-level inspector node (overlay, panel, menu…) to the page. */\nexport function mount<T extends HTMLElement>(node: T): T {\n inspectorHost().shadowRoot!.appendChild(node);\n return node;\n}\n\n/** Sets a CSS variable every inspector node can read (see styles/tokens.css). */\nexport function setRootVariable(name: string, value: string): void {\n inspectorHost().style.setProperty(name, value);\n}\n\n/**\n * True for the inspector's own UI. Events that start inside the shadow root\n * reach the page's listeners with the host element as their target.\n */\nexport function isOwn(node: EventTarget | null): boolean {\n return node instanceof Element && node.localName === HOST_TAG;\n}\n\n/** Runs `start` once the page has a `<body>` to mount into. */\nexport function whenBodyExists(start: () => void): void {\n if (document.body) start();\n else document.addEventListener(\"DOMContentLoaded\", start, { once: true });\n}\n","/** Margin, border and padding: one number per side. */\n\nexport const SIDES = [\"top\", \"right\", \"bottom\", \"left\"] as const;\nexport type Side = (typeof SIDES)[number];\nexport type Sides = Record<Side, number>;\n\n/** An element's margin, padding or border widths, in CSS pixels. */\nexport function sidesOf(cs: CSSStyleDeclaration, property: \"margin\" | \"padding\" | \"border\"): Sides {\n const suffix = property === \"border\" ? \"-width\" : \"\";\n const side = (name: Side) => parseFloat(cs.getPropertyValue(`${property}-${name}${suffix}`)) || 0;\n return { top: side(\"top\"), right: side(\"right\"), bottom: side(\"bottom\"), left: side(\"left\") };\n}\n","/** The transparent layer that hover boxes and distance lines are drawn on. */\nimport { type Box, h, placeOver } from \"../dom\";\nimport { mount } from \"../root\";\n\n/** Each kind is a CSS class in styles/overlay.css. */\nexport type BoxKind =\n | \"margin\"\n | \"border\"\n | \"padding\"\n | \"content-box\"\n | \"gap\"\n | \"child\"\n | \"target\"\n | \"measure is-x\"\n | \"measure is-y\";\n\nlet layer: HTMLElement | null = null;\n\nconst overlay = (): HTMLElement => (layer ??= mount(h(\"div\", \"overlay\")));\n\n/** Draws one box; boxes with no area are skipped. */\nexport function addBox(box: Box, kind: BoxKind): void {\n if (box.width <= 0 || box.height <= 0) return;\n placeOver(overlay().appendChild(h(\"div\", kind)), box);\n}\n\n/** Draws a distance label at a point; `transform` places it relative to that point. */\nexport function addLabel(text: string, left: number, top: number, transform: string): void {\n const label = overlay().appendChild(h(\"div\", \"measure-label\", text));\n label.style.left = `${left}px`;\n label.style.top = `${top}px`;\n label.style.transform = transform;\n}\n\nexport function clearBoxes(): void {\n layer?.replaceChildren();\n}\n","/**\n * The box-model overlay: margin (orange), border (red), padding (green),\n * content (blue), children (purple) and flex gaps (pink).\n */\nimport { type Sides, sidesOf } from \"../box-sides\";\nimport { type Box, scaleOf } from \"../dom\";\nimport { isOwn } from \"../root\";\nimport { addBox, type BoxKind } from \"./layer\";\n\n/**\n * Draws the four strips between `outer` and the rectangle inset by\n * `thickness`, and returns that inner rectangle.\n */\nfunction drawRing(outer: Box, thickness: Sides, kind: BoxKind): Box {\n const { top, right, bottom, left } = thickness;\n const innerHeight = outer.height - top - bottom;\n const rightEdge = outer.left + outer.width - right;\n const bottomEdge = outer.top + outer.height - bottom;\n addBox({ left: outer.left, top: outer.top, width: outer.width, height: top }, kind);\n addBox({ left: outer.left, top: bottomEdge, width: outer.width, height: bottom }, kind);\n addBox({ left: outer.left, top: outer.top + top, width: left, height: innerHeight }, kind);\n addBox({ left: rightEdge, top: outer.top + top, width: right, height: innerHeight }, kind);\n return {\n left: outer.left + left,\n top: outer.top + top,\n width: outer.width - left - right,\n height: innerHeight,\n };\n}\n\nexport function drawBoxModel(el: Element, rect: Box, cs: CSSStyleDeclaration): void {\n const scale = scaleOf(el, rect);\n const onScreen = (sides: Sides): Sides => ({\n top: sides.top * scale.y,\n right: sides.right * scale.x,\n bottom: sides.bottom * scale.y,\n left: sides.left * scale.x,\n });\n const margin = onScreen(sidesOf(cs, \"margin\"));\n const marginBox = {\n left: rect.left - margin.left,\n top: rect.top - margin.top,\n width: rect.width + margin.left + margin.right,\n height: rect.height + margin.top + margin.bottom,\n };\n drawRing(marginBox, margin, \"margin\");\n const paddingBox = drawRing(rect, onScreen(sidesOf(cs, \"border\")), \"border\");\n addBox(drawRing(paddingBox, onScreen(sidesOf(cs, \"padding\")), \"padding\"), \"content-box\");\n}\n\n/** Outlines the visible children and fills the gaps between flex items. */\nexport function drawChildrenAndGaps(el: Element, rect: Box, cs: CSSStyleDeclaration): void {\n const rects = Array.from(el.children)\n .filter((child) => {\n if (isOwn(child)) return false;\n const childStyle = getComputedStyle(child);\n return childStyle.display !== \"none\" && childStyle.visibility !== \"hidden\";\n })\n .map((child) => child.getBoundingClientRect());\n rects.forEach((childRect) => addBox(childRect, \"child\"));\n\n if (!cs.display.includes(\"flex\")) return;\n const isRow = cs.flexDirection.startsWith(\"row\");\n rects.sort((a, b) => (isRow ? a.left - b.left : a.top - b.top));\n for (let i = 0; i < rects.length - 1; i++) {\n const a = rects[i];\n const b = rects[i + 1];\n addBox(\n isRow\n ? { left: a.right, top: rect.top, width: b.left - a.right, height: rect.height }\n : { left: rect.left, top: a.bottom, width: rect.width, height: b.top - a.bottom },\n \"gap\",\n );\n }\n}\n","/** Red lines with the distance between the pinned element and the hovered one. */\nimport type { Scale } from \"../dom\";\nimport { addBox, addLabel } from \"./layer\";\n\n/** A label fits inside a line at least this long, in screen pixels. */\nconst ROOM_FOR_LABEL = { x: 32, y: 22 };\n\n/**\n * Where a label goes when its line is too short to hold it: beside the line,\n * and on opposite sides for two neighbouring short lines.\n */\nconst BESIDE = {\n x: [\"translate(-50%, -150%)\", \"translate(-50%, 50%)\"],\n y: [\"translate(8px, -50%)\", \"translate(calc(-100% - 8px), -50%)\"],\n};\n\nconst contains = (outer: DOMRect, inner: DOMRect): boolean =>\n outer.left <= inner.left &&\n outer.right >= inner.right &&\n outer.top <= inner.top &&\n outer.bottom >= inner.bottom;\n\n/** Where a line crosses: the middle of the shared range, else the middle of the first one. */\nfunction crossing(lo1: number, hi1: number, lo2: number, hi2: number): number {\n const lo = Math.max(lo1, lo2);\n const hi = Math.min(hi1, hi2);\n return lo < hi ? (lo + hi) / 2 : (lo1 + hi1) / 2;\n}\n\ntype Span = [from: number, to: number, axis: \"x\" | \"y\"];\n\nconst distanceText = (pixels: number): string => String(Math.round(pixels * 10) / 10);\n\n/**\n * Draws the gaps between `a` and `b`, or the four insets when one contains\n * the other. Labels are in CSS pixels: `scale` undoes the artboard zoom.\n */\nexport function drawDistances(a: DOMRect, b: DOMRect, scale: Scale): void {\n const y = crossing(a.top, a.bottom, b.top, b.bottom);\n const x = crossing(a.left, a.right, b.left, b.right);\n const insets: Span[] = [\n [Math.min(a.left, b.left), Math.max(a.left, b.left), \"x\"],\n [Math.min(a.right, b.right), Math.max(a.right, b.right), \"x\"],\n [Math.min(a.top, b.top), Math.max(a.top, b.top), \"y\"],\n [Math.min(a.bottom, b.bottom), Math.max(a.bottom, b.bottom), \"y\"],\n ];\n const gaps: Span[] = [\n [a.right, b.left, \"x\"],\n [b.right, a.left, \"x\"],\n [a.bottom, b.top, \"y\"],\n [b.bottom, a.top, \"y\"],\n ];\n\n const shortLabels = { x: 0, y: 0 };\n (contains(a, b) || contains(b, a) ? insets : gaps).forEach(([from, to, axis]) => {\n const length = to - from;\n if (length < 0.5) return;\n const middle = from + length / 2;\n const fits = length >= ROOM_FOR_LABEL[axis];\n const transform = fits ? \"translate(-50%, -50%)\" : BESIDE[axis][shortLabels[axis]++ % 2];\n if (axis === \"x\") {\n addBox({ left: from, top: y, width: length, height: 1 }, \"measure is-x\");\n addLabel(distanceText(length / scale.x), middle, y, transform);\n } else {\n addBox({ left: x, top: from, width: 1, height: length }, \"measure is-y\");\n addLabel(distanceText(length / scale.y), x, middle, transform);\n }\n });\n}\n","/** Artboards: the frames a design page lays its screens out on. */\nimport { type Box, scaleOf } from \"./dom\";\n\n/**\n * A Claude Design export stage, a design-canvas card, or any element a page\n * marks with `data-artboard`.\n */\nconst BOARD_SELECTOR = \".sa-stage, .dc-card, [data-artboard]\";\n\n/** The artboard that contains `el`, if any. */\nexport function boardOf(el: Element): Element | null {\n return el.parentElement?.closest(BOARD_SELECTOR) ?? null;\n}\n\nexport interface BoardPosition {\n /** Offset from the board's top-left corner, in the board's own pixels at any zoom. */\n x: number;\n y: number;\n /** \"x 64 · y 216\" */\n text: string;\n /** The board's name, when the page labels it. */\n board: string;\n}\n\n/** Where an element sits on its artboard. */\nexport function boardPosition(el: Element, rect: Box): BoardPosition | null {\n const board = boardOf(el);\n if (!board) return null;\n const boardRect = board.getBoundingClientRect();\n const scale = scaleOf(board, boardRect);\n const tenth = (value: number) => Math.round(value * 10) / 10;\n const x = tenth((rect.left - boardRect.left) / scale.x);\n const y = tenth((rect.top - boardRect.top) / scale.y);\n return {\n x,\n y,\n text: `x ${x} · y ${y}`,\n board: board.closest(\"[aria-label]\")?.getAttribute(\"aria-label\") ?? \"\",\n };\n}\n","/**\n * Custom elements (`<my-card tone=\"dark\">`) are components by definition:\n * the tag is the name and the attributes are the props.\n */\nimport type { ComponentReader } from \"./types\";\n\n/** How far up to look. An app shell far above the element is not \"its\" component. */\nconst MAX_DEPTH = 5;\n\nconst NOT_PROPS = new Set([\"class\", \"style\", \"id\"]);\n\nexport const customElement: ComponentReader = (el) => {\n let root: Element | null = el;\n for (let depth = 0; root && depth <= MAX_DEPTH; root = root.parentElement, depth++) {\n if (!root.localName.includes(\"-\")) continue;\n const props = Object.fromEntries(\n Array.from(root.attributes)\n .filter((attribute) => !NOT_PROPS.has(attribute.name))\n // An attribute with no value is a boolean one: <my-card open>.\n .map((attribute) => [attribute.name, attribute.value === \"\" ? true : attribute.value]),\n );\n return { name: root.localName, inside: root !== el, element: root, props };\n }\n return null;\n};\n","/**\n * Components the page declares in its markup, the way any design generator\n * or framework can:\n *\n * <div data-component=\"Card\" data-source=\"ui/Card.tsx\" data-props='{\"tone\":\"dark\"}'>\n */\nimport type { ComponentReader } from \"./types\";\n\nfunction declaredProps(root: Element): Record<string, unknown> {\n try {\n const props = JSON.parse(root.getAttribute(\"data-props\") ?? \"{}\");\n return props && typeof props === \"object\" ? props : {};\n } catch {\n return {};\n }\n}\n\nexport const declaredComponent: ComponentReader = (el) => {\n const root = el.closest(\"[data-component]\");\n const name = root?.getAttribute(\"data-component\");\n if (!root || !name) return null;\n return {\n name,\n sourcePath: root.getAttribute(\"data-source\") ?? undefined,\n inside: root !== el,\n element: root,\n props: declaredProps(root),\n };\n};\n","/**\n * Components on pages rendered with React.\n *\n * React keeps a \"fiber\" on every DOM node it renders; walking up the fibers\n * gives the components around an element. This is unofficial, so everything\n * here is defensive.\n *\n * A Claude Design export also lists its design-system components in a\n * bundle header, which gives each one its public name and source file:\n *\n * /* @ds-bundle: {\"namespace\": \"Acme\", \"components\": [{name, sourcePath}]} *\\/\n */\nimport type { ComponentInfo, ComponentReader } from \"./types\";\n\ninterface Fiber {\n type: unknown;\n return: Fiber | null;\n child: Fiber | null;\n /** The DOM node, on fibers of HTML elements. */\n stateNode: unknown;\n memoizedProps: Record<string, unknown> | null;\n}\n\ninterface ComponentType {\n displayName?: string;\n name?: string;\n styledComponentId?: string;\n}\n\ninterface BundleEntry {\n name: string;\n sourcePath?: string;\n}\n\nlet designSystem: Map<unknown, BundleEntry> | null = null;\n\n/** Design-system components by React type, read once from the bundle headers. */\nfunction designSystemComponents(): Map<unknown, BundleEntry> {\n if (designSystem) return designSystem;\n designSystem = new Map();\n for (const script of document.querySelectorAll(\"script[data-src]\")) {\n const header = /^\\/\\*\\s*@ds-bundle:\\s*(\\{.*?\\})\\s*\\*\\//s.exec(\n (script.textContent ?? \"\").slice(0, 200_000),\n );\n if (!header) continue;\n try {\n const { namespace, components } = JSON.parse(header[1]) as {\n namespace: string;\n components: BundleEntry[];\n };\n const exported = (window as unknown as Record<string, Record<string, unknown>>)[namespace];\n components.forEach((entry) => {\n if (exported?.[entry.name]) designSystem!.set(exported[entry.name], entry);\n });\n } catch {\n // A header this version does not understand.\n }\n }\n return designSystem;\n}\n\nfunction fiberOf(el: Element): Fiber | null {\n const key = Object.keys(el).find((name) => name.startsWith(\"__reactFiber$\"));\n return key ? (el as unknown as Record<string, Fiber>)[key] : null;\n}\n\n/** The first HTML element a component renders. */\nfunction rootElement(fiber: Fiber): Element | undefined {\n let node: Fiber | null = fiber;\n while (node && typeof node.type !== \"string\") node = node.child;\n return node?.stateNode instanceof Element ? node.stateNode : undefined;\n}\n\n/**\n * The nearest design-system component around an element, else the component\n * that renders the element directly.\n */\nexport const reactComponent: ComponentReader = (el) => {\n const known = designSystemComponents();\n let direct: ComponentInfo | null = null;\n let inside = false;\n let fiber = fiberOf(el)?.return ?? null;\n for (let depth = 0; fiber && depth < 40; fiber = fiber.return, depth++) {\n const type = fiber.type;\n if (typeof type === \"string\") inside = true;\n if (!type || typeof type === \"string\") continue;\n const props = fiber.memoizedProps ?? {};\n const entry = known.get(type);\n if (entry) return { ...entry, inside, props, element: rootElement(fiber) };\n\n const { displayName, name, styledComponentId } = type as ComponentType;\n // Bundlers add a number to repeated names (\"Typography2\").\n const ownName = (displayName ?? name ?? \"\").replace(/\\d+$/, \"\");\n const usable = /^[A-Z]/.test(ownName) && !/^Component$|Provider$/.test(ownName);\n if (!direct && !inside && !styledComponentId && usable) {\n direct = { name: ownName, inside, props, element: rootElement(fiber) };\n }\n }\n return direct;\n};\n","/**\n * The UI component an element belongs to, with the props the design gives it.\n *\n * Pages say this in different ways, so each way is a small \"reader\" in\n * `components/`. To support another framework, add a reader and list it\n * here; nothing else changes.\n */\nimport { customElement } from \"./components/custom-element\";\nimport { declaredComponent } from \"./components/declared\";\nimport { reactComponent } from \"./components/react\";\nimport type { ComponentInfo, ComponentReader } from \"./components/types\";\n\nexport type { ComponentInfo };\n\n/** Most explicit first: what the page declares, then what a framework knows, then the markup itself. */\nconst READERS: ComponentReader[] = [declaredComponent, reactComponent, customElement];\n\n/** The component around an element, or null when the page does not say. */\nexport function componentOf(el: Element): ComponentInfo | null {\n for (const read of READERS) {\n try {\n const component = read(el);\n if (component) return component;\n } catch {\n // A reader that trips over a page's internals must not break the panel.\n }\n }\n return null;\n}\n\nexport interface ComponentProp {\n name: string;\n value: string;\n}\n\n/**\n * The props the design passes to a component, as text. Only text, numbers\n * and booleans: functions, objects and children say little in a list.\n *\n * These are what the design uses, not what a codebase should write: the\n * inspector cannot know a project's component API, so it does not suggest\n * code.\n */\nexport function componentProps({ props }: ComponentInfo): ComponentProp[] {\n return Object.entries(props)\n .filter(([name]) => ![\"children\", \"className\", \"style\"].includes(name))\n .filter(([, value]) => [\"string\", \"number\", \"boolean\"].includes(typeof value))\n .map(([name, value]) => ({ name, value: String(value) }));\n}\n","/** How the inspector names an element in its UI. */\nimport { type Box, layoutSize } from \"./dom\";\nimport { componentOf } from \"./specs/component\";\n\n/**\n * The name a developer knows the element by: its component when it is the\n * root of one (\"Card\"), otherwise its tag.\n */\nexport function elementName(el: Element): string {\n const component = componentOf(el);\n return component && !component.inside ? component.name : el.localName;\n}\n\n/** \"444 × 382\": the element's size in its own CSS pixels. */\nexport function sizeText(el: Element, rect: Box = el.getBoundingClientRect()): string {\n const { width, height } = layoutSize(el, rect);\n return `${width} × ${height}`;\n}\n\n/**\n * The tag and the classes of an element as two spans, so the tag reads first:\n * `<span>div<span class=\"classes\">.card</span></span>`.\n */\nexport function tagAndClasses(el: Element, className: string): HTMLElement {\n const name = document.createElement(\"span\");\n name.className = className;\n name.textContent = el.localName;\n const first = el.classList[0];\n if (first) {\n const classes = name.appendChild(document.createElement(\"span\"));\n classes.className = \"classes\";\n classes.textContent = \".\" + first;\n }\n return name;\n}\n","/** The chip that names the hovered element: name, size, position on its board. */\nimport { boardPosition } from \"../board\";\nimport { type Box, h } from \"../dom\";\nimport { elementName, sizeText } from \"../element-name\";\nimport { mount } from \"../root\";\n\n/** Space between the chip and the element, and between the chip and the window edge. */\nconst GAP = 6;\nconst EDGE = 4;\n\nlet tooltip: HTMLElement | null = null;\n\n/** Shows the chip above the element's top-left corner, or below when there is no room. */\nexport function showTooltip(el: Element, rect: Box): void {\n tooltip ??= mount(h(\"div\", \"tooltip\"));\n const position = boardPosition(el, rect)?.text ?? \"\";\n tooltip.replaceChildren(\n h(\"b\", \"\", elementName(el)),\n h(\"span\", \"\", sizeText(el, rect)),\n h(\"span\", \"position\", position),\n );\n tooltip.style.display = \"flex\";\n\n const above = rect.top - tooltip.offsetHeight - GAP;\n const top = above >= EDGE ? above : rect.top + rect.height + GAP;\n const maxLeft = window.innerWidth - tooltip.offsetWidth - EDGE;\n tooltip.style.top = `${top}px`;\n tooltip.style.left = `${Math.max(EDGE, Math.min(rect.left, maxLeft))}px`;\n}\n\nexport function hideTooltip(): void {\n if (tooltip) tooltip.style.display = \"none\";\n}\n","/**\n * Inspect mode: hold Alt, or lock it on from the launcher.\n *\n * hover the hovered element's box model and a name chip, or, when an\n * element is pinned, the distances from it to the hovered one\n * click pin the hovered element\n */\nimport { scaleOf } from \"../dom\";\nimport { isOwn } from \"../root\";\nimport { pin, pinnedElement } from \"../selection\";\nimport { drawBoxModel, drawChildrenAndGaps } from \"./box-model\";\nimport { drawDistances } from \"./distances\";\nimport { addBox, clearBoxes } from \"./layer\";\nimport { hideTooltip, showTooltip } from \"./tooltip\";\n\nlet altHeld = false;\nlet locked = false;\nlet hovered: Element | null = null;\n\nexport const isInspecting = (): boolean => altHeld || locked;\n\nfunction clearHover(): void {\n clearBoxes();\n hideTooltip();\n hovered = null;\n}\n\n/** Shows or hides inspect mode after `altHeld` or `locked` changed. */\nfunction update(): void {\n document.body.style.cursor = isInspecting() ? \"crosshair\" : \"\";\n if (!isInspecting()) clearHover();\n}\n\n/** Keeps inspect mode on without holding Alt. */\nexport function setInspectLock(on: boolean): void {\n locked = on;\n update();\n}\n\nfunction inspect(target: EventTarget | null): void {\n if (target === hovered || isOwn(target)) return;\n if (\n !(target instanceof Element) ||\n target === document.body ||\n target === document.documentElement\n ) {\n clearHover();\n return;\n }\n clearBoxes();\n hovered = target;\n\n const rect = target.getBoundingClientRect();\n const pinned = pinnedElement();\n if (pinned && pinned !== target && pinned.isConnected) {\n // Measuring: only the target's outline and the distances. The box model\n // would bury the lines.\n const pinnedRect = pinned.getBoundingClientRect();\n addBox(rect, \"target\");\n drawDistances(pinnedRect, rect, scaleOf(pinned, pinnedRect));\n } else {\n const cs = getComputedStyle(target);\n drawBoxModel(target, rect, cs);\n drawChildrenAndGaps(target, rect, cs);\n }\n showTooltip(target, rect);\n}\n\nexport function startHover(): void {\n const setAlt = (held: boolean) => {\n altHeld = held;\n update();\n };\n document.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Alt\") setAlt(true);\n });\n document.addEventListener(\"keyup\", (e) => {\n if (e.key === \"Alt\") setAlt(false);\n });\n // The Alt keyup never arrives when the window loses focus first.\n window.addEventListener(\"blur\", () => setAlt(false));\n\n document.addEventListener(\n \"mousemove\",\n (e) => {\n if (isInspecting()) inspect(e.target);\n },\n true,\n );\n document.addEventListener(\"mouseleave\", () => {\n if (isInspecting()) clearHover();\n });\n\n // A click pins. The whole press is kept from the page, not only the click.\n for (const type of [\"pointerdown\", \"pointerup\", \"mousedown\", \"mouseup\", \"click\"] as const) {\n document.addEventListener(\n type,\n (e) => {\n if (!isInspecting() || isOwn(e.target)) return;\n e.preventDefault();\n e.stopPropagation();\n if (type === \"click\" && e.target instanceof Element) pin(e.target);\n },\n true,\n );\n }\n}\n","/** How this machine's keyboard names its modifier keys. */\n\nconst isApple = /Mac|iPhone|iPad/.test(navigator.platform);\n\n/** The key that turns inspect mode on. */\nexport const ALT_KEY = isApple ? \"⌥\" : \"Alt\";\n\n/** The key used with Enter to apply a content edit. */\nexport const COMMAND_KEY = isApple ? \"⌘\" : \"Ctrl\";\n","/**\n * The launcher: a pill at the bottom right that says the inspector is there.\n * Click it to lock inspect mode on, so hovering and clicking work without\n * holding Alt; click its ✕ or press Esc to turn the lock off.\n */\nimport { h } from \"./dom\";\nimport { icon } from \"./icons\";\nimport { setInspectLock } from \"./overlay/hover\";\nimport { ALT_KEY } from \"./platform\";\nimport { mount, whenBodyExists } from \"./root\";\n\nexport function startLauncher(): void {\n whenBodyExists(() => {\n const pill = mount(h(\"div\", \"launcher\"));\n pill.setAttribute(\"role\", \"button\");\n pill.title = \"Lock inspect mode on\";\n const hold = h(\"span\", \"hold\", \"hold\");\n hold.appendChild(h(\"kbd\", \"\", ALT_KEY));\n const off = h(\"button\", \"off\");\n off.setAttribute(\"aria-label\", \"Turn off inspect\");\n off.appendChild(icon(\"close\", 12));\n pill.append(\n icon(\"inspect\"),\n h(\"span\", \"\", \"Inspect\"),\n h(\"span\", \"dot\", \"·\"),\n hold,\n h(\"span\", \"locked\", \"Locked on\"),\n off,\n );\n\n const setLocked = (locked: boolean) => {\n pill.classList.toggle(\"is-locked\", locked);\n setInspectLock(locked);\n };\n pill.addEventListener(\"click\", () => setLocked(true));\n off.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n setLocked(false);\n });\n document.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") setLocked(false);\n });\n });\n}\n","/** A CSS selector that finds the same element again after a reload. */\n\n/**\n * The path from `<body>`, or from the nearest ancestor with an id.\n * Classes are kept so an edit never lands on a different element that only\n * sits at the same position.\n */\nexport function getSelector(el: Element): string {\n const parts: string[] = [];\n for (\n let node = el;\n node.parentElement && node !== document.body;\n node = node.parentElement\n ) {\n const id = node.getAttribute(\"id\");\n if (id) return [\"#\" + CSS.escape(id), ...parts].join(\" > \");\n const sameTag = Array.from(node.parentElement.children).filter(\n (sibling) => sibling.localName === node.localName,\n );\n const classes = Array.from(node.classList, (name) => \".\" + CSS.escape(name)).join(\"\");\n const nth = sameTag.length > 1 ? `:nth-of-type(${sameTag.indexOf(node) + 1})` : \"\";\n parts.unshift(CSS.escape(node.localName) + classes + nth);\n }\n return [\"body\", ...parts].join(\" > \");\n}\n","/** Links that reopen the page with an element pinned: `?inspect=<selector>`. */\nimport { getSelector } from \"./edits/selector\";\nimport { isOwn } from \"./root\";\nimport { pin } from \"./selection\";\n\nconst PARAM = \"inspect\";\n\n/** A link to this page with `el` pinned, scrolled to the element's board. */\nexport function elementLink(el: Element): string {\n const url = new URL(location.href);\n url.searchParams.set(PARAM, getSelector(el));\n const board = el.closest(\"section[id]\");\n if (board) url.hash = board.id;\n return url.href;\n}\n\n/**\n * Pins the element a shared link points to. Pages render boards late, so\n * this keeps looking for a while (15 seconds) before giving up.\n */\nexport function pinFromLink(): void {\n const selector = new URLSearchParams(location.search).get(PARAM);\n if (!selector) return;\n let tries = 0;\n const timer = setInterval(() => {\n let el: Element | null = null;\n try {\n el = document.querySelector(selector);\n } catch {\n // Not a valid selector.\n }\n if (el && !isOwn(el)) {\n el.scrollIntoView({ block: \"center\" });\n pin(el);\n }\n if (el || ++tries > 60) clearInterval(timer);\n }, 250);\n}\n","/** What the user has switched on in the panel. Modules read these when they render. */\n\nexport type Unit = \"px\" | \"rem\";\n\nexport const options = {\n /** How the Specs tab shows and copies lengths. */\n unit: \"px\" as Unit,\n /** Artboard pixels between grid lines; 0 turns the grid off. */\n gridStep: 0,\n /** Outline the pinned element on the page. */\n highlightPinned: true,\n /** All CSS tab: hide properties that still have their default value. */\n hideDefaults: true,\n /** All CSS tab: only show properties whose name contains this text. */\n search: \"\",\n /** Specs tab: the element the pinned one is compared with. */\n compareWith: null as Element | null,\n /** Specs tab: the last colour taken with the eyedropper. */\n pickedColor: \"\",\n};\n","/**\n * Marks that follow elements on the page: the pinned element's outline with\n * its name tag, the outline of a layer hovered in the panel, and the\n * artboard grid.\n */\nimport { boardOf } from \"./board\";\nimport { h, placeOver, scaleOf, visibleBox } from \"./dom\";\nimport { elementName, sizeText } from \"./element-name\";\nimport { options } from \"./options\";\nimport { mount } from \"./root\";\nimport { onSelection, pinnedElement } from \"./selection\";\n\n/** Below this distance from the top of the window, the tag moves inside the outline. */\nconst TAG_HEIGHT = 18;\n\nlet pinnedOutline: HTMLElement | null = null;\nlet pinTag: HTMLElement | null = null;\nlet hoverOutline: HTMLElement | null = null;\nlet grid: HTMLElement | null = null;\nlet outlined: Element | null = null;\nlet frame = 0;\n\n/**\n * The grid covers the visible part of the pinned element's artboard and\n * scales with it. Its lines start at the board's corner, also when the page\n * has scrolled that corner out of view.\n */\nfunction placeGrid(pinned: Element): void {\n grid ??= mount(h(\"div\", \"grid\"));\n const board = boardOf(pinned) ?? document.body;\n const rect = board.getBoundingClientRect();\n const scale = scaleOf(board, rect);\n const visible = visibleBox(board, rect);\n placeOver(grid, visible);\n const origin = `${rect.left - visible.left}px ${rect.top - visible.top}px`;\n grid.style.setProperty(\"-webkit-mask-position\", origin);\n grid.style.setProperty(\"mask-position\", origin);\n grid.classList.toggle(\"is-8\", options.gridStep === 8);\n const cell = `${options.gridStep * scale.x}px ${options.gridStep * scale.y}px`;\n grid.style.setProperty(\"-webkit-mask-size\", cell);\n grid.style.setProperty(\"mask-size\", cell);\n}\n\n/** Keeps the outline and the grid on their targets while the page scrolls or zooms. */\nfunction tick(): void {\n const pinned = pinnedElement();\n const showGrid = options.gridStep > 0 && pinned !== null;\n if (outlined && pinnedOutline) {\n const rect = outlined.getBoundingClientRect();\n placeOver(pinnedOutline, rect);\n pinTag?.classList.toggle(\"is-inside\", rect.top < TAG_HEIGHT);\n // The page may replace the pinned element (a re-render, another page).\n pinnedOutline.style.visibility = outlined.isConnected ? \"\" : \"hidden\";\n }\n if (showGrid) placeGrid(pinned);\n if (grid) grid.style.display = showGrid ? \"block\" : \"none\";\n frame = outlined || showGrid ? requestAnimationFrame(tick) : 0;\n}\n\nfunction startTicking(): void {\n if (!frame) frame = requestAnimationFrame(tick);\n}\n\n/** Outlines `el` as the pinned element, unless the user switched outlines off. */\nexport function highlight(el: Element): void {\n if (!options.highlightPinned) return;\n if (!pinnedOutline) {\n pinnedOutline = mount(h(\"div\", \"outline outline-pinned\"));\n pinTag = pinnedOutline.appendChild(h(\"div\", \"pin-tag\"));\n }\n pinTag?.replaceChildren(h(\"b\", \"\", elementName(el)), sizeText(el));\n pinnedOutline.style.display = \"block\";\n outlined = el;\n startTicking();\n}\n\n/** Brings the outline and the grid in line with the selection and the options. */\nexport function syncOutlines(): void {\n const pinned = pinnedElement();\n if (pinned && options.highlightPinned) {\n highlight(pinned);\n } else {\n if (pinnedOutline) pinnedOutline.style.display = \"none\";\n outlined = null;\n }\n startTicking();\n}\n\n/** A dashed outline for a layer the pointer is over in the panel or a menu. */\nexport function showHoverOutline(el: Element): void {\n hoverOutline ??= mount(h(\"div\", \"outline outline-hover\"));\n placeOver(hoverOutline, el.getBoundingClientRect());\n hoverOutline.style.display = \"block\";\n}\n\nexport function hideHoverOutline(): void {\n if (hoverOutline) hoverOutline.style.display = \"none\";\n}\n\nexport function startOutlines(): void {\n onSelection(() => {\n syncOutlines();\n hideHoverOutline();\n });\n}\n","/**\n * Alt + right-click: a menu of every layer under the pointer, topmost first,\n * to reach an element that sits behind another one.\n */\nimport { h } from \"../dom\";\nimport { sizeText, tagAndClasses } from \"../element-name\";\nimport { hideHoverOutline, showHoverOutline } from \"../outlines\";\nimport { isOwn, mount } from \"../root\";\nimport { pin } from \"../selection\";\nimport { isInspecting } from \"./hover\";\n\n/** The menu opens this far below and right of the pointer. */\nconst OFFSET = 8;\n\nlet menu: HTMLElement | null = null;\n\nfunction closeMenu(): void {\n if (!menu) return;\n menu.remove();\n menu = null;\n hideHoverOutline();\n}\n\nfunction openMenu(pointerX: number, pointerY: number): void {\n closeMenu();\n const layers = document\n .elementsFromPoint(pointerX, pointerY)\n .filter((el) => !isOwn(el) && el !== document.body && el !== document.documentElement);\n\n const list = (menu = mount(h(\"div\", \"menu\")));\n list.setAttribute(\"role\", \"menu\");\n const head = list.appendChild(h(\"div\", \"menu-head\"));\n head.append(h(\"span\", \"\", \"Layers at pointer\"), h(\"span\", \"count\", String(layers.length)));\n for (const layer of layers) {\n const item = list.appendChild(h(\"div\", \"menu-item\"));\n item.setAttribute(\"role\", \"menuitem\");\n item.append(tagAndClasses(layer, \"layer-name\"), h(\"span\", \"size\", sizeText(layer)));\n item.addEventListener(\"mouseenter\", () => showHoverOutline(layer));\n item.addEventListener(\"click\", () => {\n closeMenu();\n pin(layer);\n });\n }\n // Below and right of the pointer, pulled back inside the window when needed.\n const left = Math.min(pointerX + OFFSET, window.innerWidth - list.offsetWidth - OFFSET);\n const top = Math.min(pointerY + OFFSET, window.innerHeight - list.offsetHeight - OFFSET);\n list.style.left = `${left}px`;\n list.style.top = `${top}px`;\n}\n\nexport function startLayerMenu(): void {\n document.addEventListener(\n \"contextmenu\",\n (e) => {\n if (!isInspecting() || isOwn(e.target)) return;\n e.preventDefault();\n e.stopPropagation();\n openMenu(e.clientX, e.clientY);\n },\n true,\n );\n // A press anywhere else closes the menu.\n document.addEventListener(\n \"mousedown\",\n (e) => {\n if (menu && !e.composedPath().includes(menu)) closeMenu();\n },\n true,\n );\n}\n","/**\n * Compares two Claude Design exports, board by board.\n *\n * An export embeds its boards as JSON in `<script id=\"dc-data\">`:\n * `boards` lists them and `sources` holds each board's source by file name.\n */\n\nexport interface ExportBoard {\n id: string;\n file: string;\n title: string;\n}\n\nexport interface ExportData {\n /** The export date, as the export prints it. */\n exported: string;\n boards: ExportBoard[];\n sources: Record<string, string>;\n}\n\nexport interface ChangedBoard {\n title: string;\n /** The board's id on the current page, to scroll to it. */\n id?: string;\n added: string[];\n removed: string[];\n}\n\nexport interface ExportDiff {\n changed: ChangedBoard[];\n added: string[];\n removed: string[];\n}\n\nconst EMBEDDED_DATA = /<script type=\"application\\/json\" id=\"dc-data\">([\\s\\S]*?)<\\/script>/;\n\nfunction readExport(json: string | null | undefined): ExportData | null {\n try {\n const data = JSON.parse(json ?? \"\");\n const valid = data && typeof data.sources === \"object\" && Array.isArray(data.boards);\n return valid ? data : null;\n } catch {\n return null;\n }\n}\n\n/** The data of an export file's HTML, or null when the file is not an export. */\nexport const parseExport = (html: string): ExportData | null =>\n readExport(EMBEDDED_DATA.exec(html)?.[1]);\n\n/** The data of the export this page is, or null when the page is not one. */\nexport const currentExport = (): ExportData | null =>\n readExport(document.getElementById(\"dc-data\")?.textContent);\n\nfunction lineCounts(text: string): Map<string, number> {\n const counts = new Map<string, number>();\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n if (trimmed) counts.set(trimmed, (counts.get(trimmed) ?? 0) + 1);\n }\n return counts;\n}\n\n/**\n * Lines one text has more of than the other.\n * shortcut: order is ignored, so a line that only moved is not reported;\n * upgrade to a real diff if moved lines matter.\n */\nfunction extraLines(counts: Map<string, number>, otherCounts: Map<string, number>): string[] {\n return [...counts].filter(([line, n]) => n > (otherCounts.get(line) ?? 0)).map(([line]) => line);\n}\n\nexport function diffExports(older: ExportData, current: ExportData): ExportDiff {\n const boardOf = (data: ExportData, file: string) => data.boards.find((b) => b.file === file);\n const titleOf = (data: ExportData, file: string) => boardOf(data, file)?.title ?? file;\n const files = Object.keys(current.sources);\n\n return {\n changed: files\n .filter((file) => file in older.sources && older.sources[file] !== current.sources[file])\n .map((file) => {\n const before = lineCounts(older.sources[file]);\n const after = lineCounts(current.sources[file]);\n return {\n title: titleOf(current, file),\n id: boardOf(current, file)?.id,\n added: extraLines(after, before),\n removed: extraLines(before, after),\n };\n }),\n added: files.filter((file) => !(file in older.sources)).map((file) => titleOf(current, file)),\n removed: Object.keys(older.sources)\n .filter((file) => !(file in current.sources))\n .map((file) => titleOf(older, file)),\n };\n}\n","/** What every panel tab provides, and helpers to reach the panel's markup. */\nimport type { SelectionChange } from \"../selection\";\n\nexport interface Tab {\n /** Wires the tab's controls. Called once, when the panel is created. */\n mount(panel: HTMLElement): void;\n /** Shows the pinned element. Called on every pin and every change. */\n render(el: Element, change: SelectionChange): void;\n}\n\n/** An element of panel.html. Throws when the markup and the code disagree. */\nexport function part<T extends HTMLElement = HTMLElement>(panel: HTMLElement, selector: string): T {\n const node = panel.querySelector<T>(selector);\n if (!node) throw new Error(`groma: panel.html has no \"${selector}\"`);\n return node;\n}\n\n/** The scrolling body of a tab. */\nexport const tabBody = (panel: HTMLElement, tab: string): HTMLElement =>\n part(panel, `.body[data-for=\"${tab}\"]`);\n","/**\n * The Changes tab: this Claude Design export against an older one.\n *\n * Pick the older export file; the tab lists the boards that changed, were\n * added or were removed. Click a changed board to go to it and see its\n * changed lines. The tab is hidden on pages that are not exports.\n */\nimport { h } from \"../dom\";\nimport {\n type ChangedBoard,\n currentExport,\n diffExports,\n type ExportData,\n parseExport,\n} from \"../export-diff\";\nimport { icon } from \"../icons\";\nimport { part, type Tab, tabBody } from \"./tab\";\n\nconst MAX_LINES = 30;\nconst MAX_LINE_LENGTH = 160;\n\nlet head: HTMLElement;\nlet body: HTMLElement;\nlet fileInput: HTMLInputElement;\n\nconst chooseFile = (): void => fileInput.click();\n\n/** \"08 · RentSummaryCard\" with its number muted, so the name reads first. */\nfunction boardName(title: string): HTMLElement {\n const name = h(\"span\", \"board-name\");\n const [prefix, ...rest] = title.split(\" · \");\n if (rest.length) name.append(h(\"span\", \"prefix\", prefix + \" · \"), rest.join(\" · \"));\n else name.textContent = title;\n return name;\n}\n\n/** The button that shows the chosen file and lets the user pick another. */\nfunction fileButton(fileName: string, isError: boolean): HTMLElement {\n const button = h(\"button\", isError ? \"file-button is-error\" : \"file-button\");\n button.append(icon(\"file\"), h(\"span\", \"file-name\", fileName), h(\"span\", \"change\", \"Change\"));\n button.addEventListener(\"click\", chooseFile);\n return button;\n}\n\nfunction groupHeading(title: string, count: number): void {\n const heading = body.appendChild(h(\"div\", \"change-group\"));\n heading.append(icon(\"chevronDown\", 12), title, h(\"span\", \"count\", String(count)));\n}\n\n/** A changed board: click to open its lines and to scroll the page to it. */\nfunction changedRow(board: ChangedBoard): void {\n const row = body.appendChild(h(\"button\", \"change-row\"));\n row.setAttribute(\"aria-expanded\", \"false\");\n const caret = row.appendChild(h(\"span\", \"caret\"));\n caret.appendChild(icon(\"chevronRight\", 12));\n row.appendChild(boardName(board.title));\n const totals = row.appendChild(h(\"span\", \"totals\"));\n const total = (count: number, sign: string, className: string) =>\n totals.appendChild(h(\"span\", count ? className : \"zero\", sign + count));\n total(board.added.length, \"+\", \"added\");\n total(board.removed.length, \"−\", \"removed\");\n\n const lines = body.appendChild(h(\"div\", \"change-lines\"));\n lines.hidden = true;\n const addLines = (list: string[], className: string, sign: string) =>\n list.slice(0, MAX_LINES).forEach((text) => {\n const line = lines.appendChild(h(\"div\", `change-line ${className}`));\n line.append(h(\"span\", \"sign\", sign), h(\"span\", \"text\", text.slice(0, MAX_LINE_LENGTH)));\n });\n // Signs as well as colours, so the meaning never rests on colour alone.\n addLines(board.added, \"is-added\", \"+\");\n addLines(board.removed, \"is-removed\", \"−\");\n\n row.addEventListener(\"click\", () => {\n lines.hidden = !lines.hidden;\n row.setAttribute(\"aria-expanded\", String(!lines.hidden));\n caret.replaceChildren(icon(lines.hidden ? \"chevronRight\" : \"chevronDown\", 12));\n if (board.id) location.hash = board.id;\n });\n}\n\n/** A board that only exists in one of the two exports. */\nfunction plainRow(title: string, className: string, sign: string): void {\n const row = body.appendChild(h(\"div\", `change-row ${className}`));\n row.append(h(\"span\", \"sign\", sign), boardName(title));\n}\n\nfunction renderEmpty(): void {\n head.hidden = true;\n const empty = h(\"div\", \"changes-empty\");\n const choose = h(\"button\", \"primary-button\");\n choose.append(icon(\"file\"), \"Choose export\");\n choose.addEventListener(\"click\", chooseFile);\n empty.append(\n h(\"b\", \"\", \"No export loaded\"),\n h(\"span\", \"hint\", \"Pick an earlier export of this page to see which boards changed.\"),\n choose,\n );\n body.replaceChildren(empty);\n}\n\nfunction renderError(fileName: string): void {\n head.hidden = false;\n head.replaceChildren(fileButton(fileName, true));\n const alert = h(\"div\", \"alert\");\n alert.setAttribute(\"role\", \"alert\");\n const text = h(\"div\");\n text.append(\n h(\"b\", \"\", \"Not an export\"),\n h(\"span\", \"hint\", \"This file has no inspector data. Pick an .html file saved with Export from this page.\"),\n );\n alert.append(icon(\"warning\"), text);\n body.replaceChildren(alert);\n}\n\nfunction renderChanges(fileName: string, older: ExportData, current: ExportData): void {\n const diff = diffExports(older, current);\n head.hidden = false;\n const dates = h(\"div\", \"dates\");\n dates.append(h(\"span\", \"\", older.exported), icon(\"arrow\", 12), h(\"span\", \"\", current.exported));\n head.replaceChildren(fileButton(fileName, false), dates);\n\n body.replaceChildren();\n if (diff.changed.length) groupHeading(\"Changed\", diff.changed.length);\n diff.changed.forEach(changedRow);\n if (diff.added.length) groupHeading(\"Added\", diff.added.length);\n diff.added.forEach((title) => plainRow(title, \"is-added\", \"+\"));\n if (diff.removed.length) groupHeading(\"Removed\", diff.removed.length);\n diff.removed.forEach((title) => plainRow(title, \"is-removed\", \"−\"));\n if (!body.childElementCount) {\n body.appendChild(h(\"div\", \"changes-empty\")).appendChild(h(\"span\", \"hint\", \"No board changed.\"));\n }\n}\n\nexport const changesTab: Tab = {\n mount(panel) {\n head = part(panel, \".changes-head\");\n body = tabBody(panel, \"changes\");\n fileInput = part<HTMLInputElement>(panel, 'input[type=\"file\"]');\n part(panel, '.tab[data-tab=\"changes\"]').hidden = !currentExport();\n\n fileInput.addEventListener(\"change\", async () => {\n const file = fileInput.files?.[0];\n fileInput.value = \"\";\n if (!file) return;\n const older = parseExport(await file.text());\n const current = currentExport();\n if (older && current) renderChanges(file.name, older, current);\n else renderError(file.name);\n });\n renderEmpty();\n },\n // The comparison does not depend on the pinned element.\n render() {},\n};\n","/** Colour helpers for the Specs tab. */\n\nlet probe: CanvasRenderingContext2D | null = null;\nconst normalized = new Map<string, string | null>();\n\n/**\n * One spelling per colour, so \"#fff\", \"white\" and \"rgb(255, 255, 255)\"\n * compare equal. Returns null for text that is not a colour.\n */\nfunction normalizeColor(value: string): string | null {\n let result = normalized.get(value);\n if (result === undefined) {\n probe ??= document.createElement(\"canvas\").getContext(\"2d\")!;\n // An invalid colour leaves fillStyle untouched, so two different\n // starting points give two different answers.\n probe.fillStyle = \"#000\";\n probe.fillStyle = value;\n const first = probe.fillStyle;\n probe.fillStyle = \"#fff\";\n probe.fillStyle = value;\n result = first === probe.fillStyle ? first : null;\n normalized.set(value, result);\n }\n return result;\n}\n\n/** The CSS variables on an element whose value is `color`, shortest names first. */\nexport function tokensWithColor(cs: CSSStyleDeclaration, color: string): string[] {\n const wanted = normalizeColor(color);\n return Array.from(cs)\n .filter((prop) => prop.startsWith(\"--\"))\n .filter((prop) => normalizeColor(cs.getPropertyValue(prop).trim()) === wanted)\n .sort((a, b) => a.length - b.length);\n}\n\n/** \"rgb(27, 26, 23)\" -> \"#1B1A17\". A translucent colour keeps its alpha as a percentage. */\nexport function formatColor(value: string): string {\n const parts = /^rgba?\\((\\d+), (\\d+), (\\d+)(?:, ([\\d.]+))?\\)$/.exec(value);\n if (!parts) return value;\n const hex = parts\n .slice(1, 4)\n .map((channel) => Number(channel).toString(16).padStart(2, \"0\"))\n .join(\"\")\n .toUpperCase();\n const alpha = parts[4] === undefined ? \"\" : ` ${Math.round(Number(parts[4]) * 100)}%`;\n return \"#\" + hex + alpha;\n}\n\n/** True when a CSS value starts like a colour, to decide whether to show a swatch. */\nexport const isColorValue = (value: string): boolean =>\n /^(#|rgb|rgba|hsl|hsla|oklch|color\\()/.test(value.trim());\n","/** The CSS properties the All CSS tab lists, in groups, and their default values. */\n\nexport const CSS_GROUPS: { label: string; props: string[] }[] = [\n {\n label: \"Display & Position\",\n props: [\n \"display\",\n \"position\",\n \"top\",\n \"right\",\n \"bottom\",\n \"left\",\n \"z-index\",\n \"visibility\",\n \"overflow\",\n \"overflow-x\",\n \"overflow-y\",\n ],\n },\n {\n label: \"Box Model\",\n props: [\n \"width\",\n \"height\",\n \"min-width\",\n \"max-width\",\n \"min-height\",\n \"max-height\",\n \"box-sizing\",\n \"margin-top\",\n \"margin-right\",\n \"margin-bottom\",\n \"margin-left\",\n \"border-top-width\",\n \"border-right-width\",\n \"border-bottom-width\",\n \"border-left-width\",\n \"border-style\",\n \"border-color\",\n \"border-radius\",\n \"padding-top\",\n \"padding-right\",\n \"padding-bottom\",\n \"padding-left\",\n ],\n },\n {\n label: \"Flex (container)\",\n props: [\n \"flex-direction\",\n \"flex-wrap\",\n \"justify-content\",\n \"align-items\",\n \"align-content\",\n \"gap\",\n \"row-gap\",\n \"column-gap\",\n ],\n },\n {\n label: \"Flex (item)\",\n props: [\"flex\", \"flex-grow\", \"flex-shrink\", \"flex-basis\", \"align-self\", \"justify-self\", \"order\"],\n },\n {\n label: \"Grid (container)\",\n props: [\n \"grid-template-columns\",\n \"grid-template-rows\",\n \"grid-template-areas\",\n \"grid-auto-columns\",\n \"grid-auto-rows\",\n \"grid-auto-flow\",\n ],\n },\n { label: \"Grid (item)\", props: [\"grid-column\", \"grid-row\", \"grid-area\"] },\n {\n label: \"Typography\",\n props: [\n \"font-family\",\n \"font-size\",\n \"font-weight\",\n \"font-style\",\n \"line-height\",\n \"letter-spacing\",\n \"text-align\",\n \"text-transform\",\n \"text-decoration\",\n \"white-space\",\n \"color\",\n ],\n },\n {\n label: \"Background\",\n props: [\n \"background-color\",\n \"background-image\",\n \"background-size\",\n \"background-position\",\n \"background-repeat\",\n ],\n },\n {\n label: \"Transform & Transition\",\n props: [\"transform\", \"transform-origin\", \"transition\", \"animation\", \"opacity\", \"pointer-events\"],\n },\n];\n\n/** A property showing this value is dimmed, and hidden by \"Hide defaults\". */\nexport const DEFAULT_VALUES: Record<string, string> = {\n position: \"static\",\n top: \"auto\",\n right: \"auto\",\n bottom: \"auto\",\n left: \"auto\",\n \"z-index\": \"auto\",\n overflow: \"visible\",\n \"overflow-x\": \"visible\",\n \"overflow-y\": \"visible\",\n visibility: \"visible\",\n \"flex-direction\": \"row\",\n \"flex-wrap\": \"nowrap\",\n \"justify-content\": \"normal\",\n \"align-items\": \"normal\",\n \"align-content\": \"normal\",\n \"flex-grow\": \"0\",\n \"flex-shrink\": \"1\",\n \"flex-basis\": \"auto\",\n \"align-self\": \"auto\",\n \"justify-self\": \"auto\",\n order: \"0\",\n \"grid-template-columns\": \"none\",\n \"grid-template-rows\": \"none\",\n \"grid-template-areas\": \"none\",\n \"grid-auto-columns\": \"auto\",\n \"grid-auto-rows\": \"auto\",\n \"grid-auto-flow\": \"row\",\n \"grid-column\": \"auto\",\n \"grid-row\": \"auto\",\n \"grid-area\": \"auto\",\n transform: \"none\",\n animation: \"none\",\n \"pointer-events\": \"auto\",\n \"text-decoration\": \"none solid rgb(0, 0, 0)\",\n \"text-transform\": \"none\",\n \"font-style\": \"normal\",\n \"letter-spacing\": \"normal\",\n \"background-image\": \"none\",\n};\n","/**\n * The All CSS tab: every computed property of the pinned element.\n *\n * Values are editable. An edit is applied to the page at once, saved, and\n * replayed on the next load (see edits/overrides.ts). The top of the tab\n * edits the element's text or form value.\n */\nimport { h } from \"../dom\";\nimport {\n applyContent,\n applyStyle,\n getContent,\n hasSavedContent,\n hasValue,\n restoreContent,\n restoreStyle,\n saveContent,\n savedStyles,\n saveStyle,\n} from \"../edits/overrides\";\nimport { getSelector } from \"../edits/selector\";\nimport { options } from \"../options\";\nimport { syncOutlines } from \"../outlines\";\nimport { rerender, type SelectionChange } from \"../selection\";\nimport { isColorValue } from \"../specs/colors\";\nimport { CSS_GROUPS, DEFAULT_VALUES } from \"./css-properties\";\nimport { part, type Tab, tabBody } from \"./tab\";\n\ninterface CssRow {\n name: string;\n value: string;\n /** The value is the property's default. */\n dimmed?: boolean;\n /** The user edited this property. */\n overridden?: boolean;\n /** Listed under \"Custom styles\", the user's edits. */\n custom?: boolean;\n}\n\nlet body: HTMLElement;\n/** The row the user was moving to when an edit caused a re-render. */\nlet focusAfterRender = \"\";\n\n// ── read-only groups ───────────────────────────────────────────\n\nfunction elementRows(el: Element): CssRow[] {\n return [\n { name: \"tag\", value: el.localName },\n { name: \"id\", value: el.id ? \"#\" + el.id : \"—\" },\n { name: \"classes\", value: Array.from(el.classList).join(\" \") || \"—\" },\n ];\n}\n\nfunction rectRows(el: Element): CssRow[] {\n const rect = el.getBoundingClientRect();\n return ([\"top\", \"right\", \"bottom\", \"left\", \"width\", \"height\"] as const).map((name) => ({\n name,\n value: rect[name].toFixed(2) + \"px\",\n }));\n}\n\nfunction variableRows(cs: CSSStyleDeclaration): CssRow[] {\n return Array.from(cs)\n .filter((prop) => prop.startsWith(\"--\"))\n .map((name) => ({ name, value: cs.getPropertyValue(name).trim() }));\n}\n\n// ── rows ───────────────────────────────────────────────────────\n\nfunction rowShell(row: CssRow): { rowEl: HTMLElement; valueEl: HTMLElement } {\n const rowEl = h(\"div\", \"row\");\n rowEl.classList.toggle(\"is-overridden\", !!row.overridden);\n rowEl.classList.toggle(\"is-dimmed\", !!row.dimmed && !row.overridden);\n rowEl.appendChild(h(\"span\", \"name\", row.name));\n if (isColorValue(row.value)) {\n rowEl.appendChild(h(\"span\", \"swatch\")).style.setProperty(\"--swatch\", row.value);\n }\n const valueEl = rowEl.appendChild(h(\"span\", \"value\", row.value || \"—\"));\n valueEl.title = row.value;\n return { rowEl, valueEl };\n}\n\nconst readOnlyRow = (row: CssRow): HTMLElement => rowShell(row).rowEl;\n\n/**\n * A row whose value can be typed over.\n * Enter or blur save Escape cancel\n * ↑ / ↓ change a number by 1 (Shift: 10, Alt: 0.1), applied live\n */\nfunction editableRow(row: CssRow, el: Element, selector: string): HTMLElement {\n const { rowEl, valueEl } = rowShell(row);\n let shown = row.value;\n let overridden = !!row.overridden;\n let nudged = false;\n\n valueEl.contentEditable = \"true\";\n valueEl.spellcheck = false;\n valueEl.dataset.row = (row.custom ? \"custom:\" : \"\") + row.name;\n\n /** Saves and applies a value. An empty value removes the edit. */\n const commit = (value: string) => {\n if (value) {\n saveStyle(selector, row.name, value);\n applyStyle(el, row.name, value);\n overridden = true;\n shown = value;\n } else if (overridden) {\n saveStyle(selector, row.name, null);\n restoreStyle(el, row.name);\n }\n };\n\n valueEl.addEventListener(\"focus\", () => {\n if (!shown) valueEl.textContent = \"\";\n });\n valueEl.addEventListener(\"blur\", (e) => {\n const typed = (valueEl.textContent ?? \"\").trim();\n if (typed === shown && !nudged) {\n valueEl.textContent = shown || \"—\";\n return;\n }\n if (typed !== shown) commit(typed);\n const next = e.relatedTarget;\n focusAfterRender = next instanceof HTMLElement ? (next.dataset.row ?? \"\") : \"\";\n rerender();\n });\n valueEl.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n valueEl.blur();\n }\n if (e.key === \"Escape\") {\n valueEl.textContent = shown;\n valueEl.blur();\n }\n const number = /^(-?\\d*\\.?\\d+)(.*)/.exec(valueEl.textContent ?? \"\");\n if (number && (e.key === \"ArrowUp\" || e.key === \"ArrowDown\")) {\n e.preventDefault();\n const step = e.shiftKey ? 10 : e.altKey ? 0.1 : 1;\n const next = parseFloat(number[1]) + (e.key === \"ArrowUp\" ? step : -step);\n const decimals = step === 0.1 || number[1].includes(\".\") ? 1 : 0;\n valueEl.textContent = next.toFixed(decimals) + number[2];\n commit(valueEl.textContent);\n nudged = true;\n }\n });\n\n if (overridden) {\n const reset = rowEl.appendChild(h(\"button\", \"reset\", \"↩\"));\n reset.title = \"Reset to the page's own value\";\n reset.addEventListener(\"click\", () => {\n commit(\"\");\n rerender();\n });\n }\n return rowEl;\n}\n\n// ── content editor ─────────────────────────────────────────────\n\n/** Elements that usually hold text directly, so editing their HTML is safe. */\nconst TEXT_TAGS = new Set(\n \"span p h1 h2 h3 h4 h5 h6 label button a li td th caption legend figcaption blockquote cite code pre time mark strong em small b i u s\".split(\n \" \",\n ),\n);\n\n/** Edits the element's text (or a form control's value). ⌘/Ctrl+Enter applies. */\nfunction contentEditor(el: Element, selector: string): HTMLElement {\n if (!hasValue(el) && !TEXT_TAGS.has(el.localName)) {\n const note = h(\"section\", \"block\");\n note.appendChild(h(\"span\", \"hint\", `${el.localName} holds other elements, so its content is not editable here.`));\n return note;\n }\n\n const section = h(\"section\", \"block content\");\n section.appendChild(h(\"div\", \"heading\", hasValue(el) ? \"Value\" : \"Content\"));\n const editor = section.appendChild(h(\"div\", \"editor\", getContent(el)));\n editor.contentEditable = \"true\";\n editor.spellcheck = false;\n const footer = section.appendChild(h(\"div\", \"footer\"));\n footer.appendChild(h(\"span\", \"hint\", \"⌘/Ctrl ↵ to apply · Esc to cancel\"));\n const reset = footer.appendChild(h(\"button\", \"reset\", \"↩ Reset\"));\n reset.hidden = !hasSavedContent(selector);\n\n editor.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n e.preventDefault();\n const content = editor.textContent ?? \"\";\n applyContent(el, content);\n saveContent(selector, content);\n reset.hidden = false;\n editor.blur();\n }\n if (e.key === \"Escape\") {\n editor.textContent = getContent(el);\n editor.blur();\n }\n });\n reset.addEventListener(\"click\", () => {\n saveContent(selector, null);\n restoreContent(el);\n rerender();\n });\n return section;\n}\n\n// ── tab ────────────────────────────────────────────────────────\n\nfunction renderCss(el: Element, change: SelectionChange): void {\n // A new element starts at the top; an edit keeps the list where it was.\n const scrollTop = change === \"pinned\" ? 0 : body.scrollTop;\n const selector = getSelector(el);\n const saved = savedStyles(selector);\n const cs = getComputedStyle(el);\n const addSection = (title: string, rows: HTMLElement[], labelClass = \"\") => {\n if (!rows.length) return;\n body.appendChild(h(\"div\", `heading ${labelClass}`, title));\n body.append(...rows);\n };\n const matchesSearch = (row: CssRow) =>\n !options.search || row.name.toLowerCase().includes(options.search);\n const editable = (row: CssRow) => editableRow(row, el, selector);\n\n body.replaceChildren(contentEditor(el, selector));\n\n const custom = Object.entries(saved).map(([name, value]) => ({\n name,\n value,\n overridden: true,\n custom: true,\n }));\n addSection(\"Your edits\", custom.map(editable), \"is-custom\");\n if (custom.length) body.appendChild(h(\"div\", \"divider\"));\n\n addSection(\"Element\", elementRows(el).filter(matchesSearch).map(readOnlyRow));\n addSection(\"Rect\", rectRows(el).filter(matchesSearch).map(readOnlyRow));\n for (const group of CSS_GROUPS) {\n const rows = group.props\n .map((name): CssRow => {\n const computed = cs.getPropertyValue(name).trim();\n const overridden = saved[name] !== undefined;\n return {\n name,\n value: overridden ? saved[name] : computed,\n dimmed: DEFAULT_VALUES[name] === computed,\n overridden,\n };\n })\n .filter((row) => row.value && matchesSearch(row))\n .filter((row) => !(options.hideDefaults && row.dimmed && !row.overridden));\n addSection(group.label, rows.map(editable));\n }\n addSection(\"CSS Variables\", variableRows(cs).filter(matchesSearch).map(editable));\n\n body.scrollTop = scrollTop;\n if (focusAfterRender) {\n const row = CSS.escape(focusAfterRender);\n focusAfterRender = \"\";\n // After the browser finishes moving focus to the row that was just replaced.\n setTimeout(() => body.querySelector<HTMLElement>(`[data-row=\"${row}\"]`)?.focus());\n }\n}\n\nfunction mountControls(panel: HTMLElement): void {\n const search = part<HTMLInputElement>(panel, \".search input\");\n const setSearch = (query: string) => {\n search.value = query;\n options.search = query.toLowerCase().trim();\n rerender();\n };\n search.addEventListener(\"input\", () => setSearch(search.value));\n search.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") setSearch(\"\");\n });\n part(panel, \".clear\").addEventListener(\"click\", () => setSearch(\"\"));\n\n part<HTMLInputElement>(panel, '[data-option=\"hideDefaults\"]').addEventListener(\"change\", (e) => {\n options.hideDefaults = (e.target as HTMLInputElement).checked;\n rerender();\n });\n part<HTMLInputElement>(panel, '[data-option=\"highlightPinned\"]').addEventListener(\"change\", (e) => {\n options.highlightPinned = (e.target as HTMLInputElement).checked;\n syncOutlines();\n });\n}\n\nexport const cssTab: Tab = {\n mount(panel) {\n body = tabBody(panel, \"css\");\n mountControls(panel);\n },\n render: renderCss,\n};\n","/**\n * The Layers tab: the element tree of the pinned element's artboard (or of\n * the page, outside an artboard), opened down to the pinned element.\n * Hover a layer to outline it on the page; click it to pin it.\n */\nimport { boardOf } from \"../board\";\nimport { h } from \"../dom\";\nimport { tagAndClasses } from \"../element-name\";\nimport { icon } from \"../icons\";\nimport { hideHoverOutline, showHoverOutline } from \"../outlines\";\nimport { isOwn } from \"../root\";\nimport { pin } from \"../selection\";\nimport { type Tab, tabBody } from \"./tab\";\n\nconst NOT_LAYERS = new Set([\"script\", \"style\", \"link\", \"meta\", \"noscript\"]);\n\nlet body: HTMLElement;\n\nconst layerChildren = (node: Element): Element[] =>\n Array.from(node.children).filter((child) => !isOwn(child) && !NOT_LAYERS.has(child.localName));\n\n/** One layer and, when open, its children. `open` holds the layers that start expanded. */\nfunction treeNode(node: Element, depth: number, open: Set<Element>, current: Element): HTMLElement {\n const wrap = h(\"div\");\n const children = layerChildren(node);\n const row = wrap.appendChild(h(\"div\", \"layer\"));\n row.setAttribute(\"role\", \"treeitem\");\n row.classList.toggle(\"is-current\", node === current);\n row.style.setProperty(\"--depth\", String(depth));\n const caret = row.appendChild(h(\"span\", \"caret\"));\n row.appendChild(tagAndClasses(node, \"layer-name\"));\n const childList = wrap.appendChild(h(\"div\"));\n\n let isOpen = false;\n const setOpen = (next: boolean) => {\n isOpen = next;\n caret.replaceChildren(icon(isOpen ? \"chevronDown\" : \"chevronRight\", 12));\n childList.replaceChildren(\n ...(isOpen ? children.map((child) => treeNode(child, depth + 1, open, current)) : []),\n );\n };\n if (children.length) setOpen(open.has(node));\n\n caret.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n if (children.length) setOpen(!isOpen);\n });\n row.addEventListener(\"mouseenter\", () => showHoverOutline(node));\n row.addEventListener(\"mouseleave\", hideHoverOutline);\n row.addEventListener(\"click\", () => {\n if (node !== current) pin(node);\n });\n return wrap;\n}\n\nfunction renderLayers(el: Element): void {\n const root = boardOf(el) ?? document.body;\n const open = new Set<Element>();\n for (let node: Element | null = el; node; node = node.parentElement) {\n // The pinned layer itself starts closed: its children are one click away.\n if (node !== el) open.add(node);\n if (node === root) break;\n }\n const scrollTop = body.scrollTop;\n body.replaceChildren(treeNode(root, 0, open, el));\n body.scrollTop = scrollTop;\n}\n\nexport const layersTab: Tab = {\n mount(panel) {\n body = tabBody(panel, \"layers\");\n },\n render: renderLayers,\n};\n","/** Panel width, open tab and unit: remembered for every page on this origin. */\nimport { readStore, writeStore } from \"../edits/storage\";\nimport { options } from \"../options\";\n\nconst LAYOUT_KEY = \"__groma_panel\";\n/** The tabs worth reopening on. The Reference tab is a detour, not a place to start. */\nconst TAB_IDS = [\"specs\", \"css\", \"layers\", \"changes\"];\n\ninterface Layout {\n width: string;\n tab: string;\n unit: string;\n}\n\nexport function loadLayout(panel: HTMLElement): void {\n const layout = readStore<Layout>(LAYOUT_KEY);\n panel.dataset.tab = layout.tab && TAB_IDS.includes(layout.tab) ? layout.tab : \"specs\";\n panel.style.width = layout.width ?? \"\";\n options.unit = layout.unit === \"rem\" ? \"rem\" : \"px\";\n}\n\nexport function saveLayout(panel: HTMLElement): void {\n const open = panel.dataset.tab ?? \"\";\n const layout: Layout = {\n width: panel.style.width,\n tab: TAB_IDS.includes(open) ? open : (readStore<Layout>(LAYOUT_KEY).tab ?? \"specs\"),\n unit: options.unit,\n };\n writeStore(LAYOUT_KEY, layout);\n}\n\nexport function setTab(panel: HTMLElement, tab: string): void {\n panel.dataset.tab = tab;\n saveLayout(panel);\n}\n","/**\n * The Reference tab: every shortcut and trick in one place, opened from the\n * » menu. Its content is reference.html.\n */\nimport { ALT_KEY, COMMAND_KEY } from \"../platform\";\nimport reference from \"./reference.html?raw\";\nimport { type Tab, tabBody } from \"./tab\";\n\nconst KEYS: Record<string, string> = { alt: ALT_KEY, command: COMMAND_KEY };\n\nexport const referenceTab: Tab = {\n mount(panel) {\n const body = tabBody(panel, \"reference\");\n body.innerHTML = reference;\n // Name the modifier keys as this keyboard does.\n for (const key of body.querySelectorAll<HTMLElement>(\"kbd[data-key]\")) {\n key.textContent = KEYS[key.dataset.key ?? \"\"] ?? \"\";\n }\n },\n // The reference does not depend on the pinned element.\n render() {},\n};\n","/** Reads the page's stylesheets: which declarations apply to an element. */\n\n/** Visits every style rule of the page, in source order. */\nexport function forEachPageRule(visit: (rule: CSSStyleRule) => void): void {\n const walk = (rules: CSSRuleList): void => {\n for (const rule of rules) {\n if (\"cssRules\" in rule) walk(rule.cssRules as CSSRuleList);\n if (rule instanceof CSSStyleRule) visit(rule);\n }\n };\n for (const sheet of document.styleSheets) {\n try {\n walk(sheet.cssRules);\n } catch {\n // Cross-origin stylesheet: its rules cannot be read.\n }\n }\n}\n\n/**\n * The declarations that apply to an element, weakest first: matching rules\n * in source order, then its inline style.\n */\nfunction appliedStyles(el: Element): CSSStyleDeclaration[] {\n const styles: CSSStyleDeclaration[] = [];\n forEachPageRule((rule) => {\n try {\n if (el.matches(rule.selectorText)) styles.push(rule.style);\n } catch {\n // A selector this browser cannot match (vendor pseudo-elements).\n }\n });\n styles.push((el as HTMLElement).style);\n return styles;\n}\n\nexport type StyleLookup = (el: Element) => CSSStyleDeclaration[];\n\n/** `appliedStyles` with a cache, for one render that asks about the same elements often. */\nexport function createStyleLookup(): StyleLookup {\n const cache = new Map<Element, CSSStyleDeclaration[]>();\n return (el) => {\n let styles = cache.get(el);\n if (!styles) cache.set(el, (styles = appliedStyles(el)));\n return styles;\n };\n}\n\n/**\n * The value an element declares for a property, or \"\" when it declares none.\n * shortcut: the last declaration wins and selector specificity is ignored;\n * upgrade if a page shows a value from a rule that a stronger rule overrides.\n */\nexport function declaredValue(styles: CSSStyleDeclaration[], prop: string): string {\n const style = styles.findLast((candidate) => candidate.getPropertyValue(prop));\n return style ? style.getPropertyValue(prop).trim() : \"\";\n}\n","/** Transitions and animations of an element, one item per property. */\n\nexport interface MotionItem {\n /** The transitioned property, or the animation's name. */\n property: string;\n duration: string;\n /** The timing function as CSS writes it. */\n easing: string;\n /** The same timing function as cubic-bezier numbers, for drawing its curve. */\n curve: number[];\n}\n\nconst KEYWORD_CURVES: Record<string, number[]> = {\n linear: [0, 0, 1, 1],\n ease: [0.25, 0.1, 0.25, 1],\n \"ease-in\": [0.42, 0, 1, 1],\n \"ease-out\": [0, 0, 0.58, 1],\n \"ease-in-out\": [0.42, 0, 0.58, 1],\n};\n\n/** \"ease-in\" or \"cubic-bezier(0.4, 0, 0.2, 1)\" as four numbers; a straight line for the rest. */\nexport function easingCurve(easing: string): number[] {\n const numbers = /^cubic-bezier\\((.+)\\)$/.exec(easing)?.[1].split(\",\").map(Number);\n return numbers?.length === 4 ? numbers : (KEYWORD_CURVES[easing] ?? KEYWORD_CURVES.linear);\n}\n\n/** Splits a CSS list on the commas that are not inside parentheses. */\nconst listItems = (value: string): string[] => value.split(/,\\s*(?![^(]*\\))/);\n\n/** Pairs up the parallel lists CSS keeps for properties, durations and easings. */\nfunction zip(properties: string, durations: string, easings: string): MotionItem[] {\n const durationList = listItems(durations);\n const easingList = listItems(easings);\n return listItems(properties)\n .map((property, i) => {\n const easing = easingList[i % easingList.length];\n return {\n property,\n duration: durationList[i % durationList.length],\n easing,\n curve: easingCurve(easing),\n };\n })\n .filter((item) => /[1-9]/.test(item.duration));\n}\n\nexport function motionItems(cs: CSSStyleDeclaration): MotionItem[] {\n const transitions = zip(cs.transitionProperty, cs.transitionDuration, cs.transitionTimingFunction);\n const animations =\n cs.animationName === \"none\"\n ? []\n : zip(cs.animationName, cs.animationDuration, cs.animationTimingFunction);\n return [...transitions, ...animations];\n}\n","/** Sizing intent, in Figma's words: a fixed size, \"hug\" the content, or \"fill\" the parent. */\nimport { declaredValue } from \"../page-styles\";\n\ntype Intent = \"fixed\" | \"hug\" | \"fill\";\n\n/** \"fill × hug\": how the element gets its width and its height. */\nexport function sizingIntent(\n el: Element,\n cs: CSSStyleDeclaration,\n styles: CSSStyleDeclaration[],\n): string {\n const parent = el.parentElement ? getComputedStyle(el.parentElement) : null;\n const inFlex = parent !== null && parent.display.includes(\"flex\");\n const rowParent = inFlex && parent.flexDirection.startsWith(\"row\");\n const grows = cs.flexGrow !== \"0\";\n const alignment = parent && cs.alignSelf === \"auto\" ? parent.alignItems : cs.alignSelf;\n const stretches = alignment === \"normal\" || alignment === \"stretch\";\n\n const intent = (prop: string, isMainAxis: boolean, fillsByDefault: boolean): Intent => {\n const declared = declaredValue(styles, prop);\n if (declared === \"100%\") return \"fill\";\n if (declared && declared !== \"auto\" && !declared.includes(\"content\")) return \"fixed\";\n if (inFlex) return (isMainAxis ? grows : stretches) ? \"fill\" : \"hug\";\n return fillsByDefault ? \"fill\" : \"hug\";\n };\n\n // Outside flex, a block-level box takes its container's width.\n const blockLevel = [\"block\", \"flex\", \"grid\", \"list-item\", \"table\"].includes(cs.display);\n const width = intent(\"width\", rowParent, blockLevel);\n const height = intent(\"height\", inFlex && !rowParent, false);\n return `${width} × ${height}`;\n}\n","/** How the Specs tab writes lengths, on screen and on the clipboard. */\nimport type { CopyFormat } from \"../config\";\nimport type { Unit } from \"../options\";\n\n/** CSS shorthand order: one value when all sides match, two for vertical/horizontal. */\nexport function compactSides([top, right, bottom, left]: string[]): string {\n if (top === right && top === bottom && top === left) return top;\n if (top === bottom && right === left) return `${top} ${right}`;\n return `${top} ${right} ${bottom} ${left}`;\n}\n\n/**\n * Rewrites every pixel length in `value` the way the Specs tab shows it:\n * a bare number in px (one MUI spacing step is 1px), or rem.\n */\nexport function shownLength(value: string, unit: Unit, rootFontSize: number): string {\n return value.replace(/(-?\\d*\\.?\\d+)px/g, (_, pixels: string) =>\n unit === \"px\" ? pixels : `${Math.round((Number(pixels) / rootFontSize) * 1000) / 1000}rem`,\n );\n}\n\n/**\n * What a click on a length copies. CSS keeps the unit (`24px`, `1.5rem`);\n * the \"sx\" format copies a bare number for pixels.\n */\nexport function copiedLength(\n value: string,\n unit: Unit,\n rootFontSize: number,\n format: CopyFormat,\n): string {\n if (unit === \"rem\") return shownLength(value, \"rem\", rootFontSize);\n return format === \"sx\" ? shownLength(value, \"px\", rootFontSize) : value;\n}\n\n/**\n * What a click on \"padding\" or \"margin\" copies, for sides given in pixels:\n * a CSS declaration (`padding: 12px 16px`), or the \"sx\" keys (`py: 12, px: 16`).\n */\nexport function copiedSpacing(\n name: \"padding\" | \"margin\",\n sides: number[],\n unit: Unit,\n rootFontSize: number,\n format: CopyFormat,\n): string {\n if (format === \"sx\" && unit === \"px\") return sxSpacing(name, sides);\n const lengths = sides.map((side) => copiedLength(`${side}px`, unit, rootFontSize, \"css\"));\n return `${name}: ${compactSides(lengths)}`;\n}\n\n/** Padding or margin as style objects take it: `p: 24`, `py: 12, px: 16`, or one key per side. */\nexport function sxSpacing(name: \"padding\" | \"margin\", sides: number[]): string {\n const [top, right, bottom, left] = sides;\n const key = name[0];\n if (top === right && top === bottom && top === left) return `${key}: ${top}`;\n if (top === bottom && right === left) return `${key}y: ${top}, ${key}x: ${right}`;\n return `${key}t: ${top}, ${key}r: ${right}, ${key}b: ${bottom}, ${key}l: ${left}`;\n}\n","/**\n * What a developer needs to build an element, grouped the way the Specs tab\n * shows it: layout, shape, text, icon, motion.\n */\nimport { boardPosition } from \"../board\";\nimport { SIDES, sidesOf } from \"../box-sides\";\nimport { layoutSize } from \"../dom\";\nimport { hasValue } from \"../edits/overrides\";\nimport type { StyleLookup } from \"../page-styles\";\nimport { motionItems } from \"./motion\";\nimport { sizingIntent } from \"./sizing\";\nimport { compactSides } from \"./units\";\n\nexport interface Spec {\n name: string;\n /** Empty when there is nothing to show; such rows are skipped. */\n value: string;\n /** The value is a colour: show a swatch and look for tokens with that colour. */\n color?: boolean;\n /** CSS properties to look up design tokens for. */\n props?: string[];\n /** Extra information shown beside the value. */\n note?: string;\n}\n\nexport interface SpecSection {\n label: string;\n rows: Spec[];\n}\n\nconst TRANSPARENT = \"rgba(0, 0, 0, 0)\";\n\n/** \"\" when `value` is one of the defaults, so the row is skipped. */\nconst unless = (value: string, ...defaults: string[]): string =>\n defaults.includes(value) ? \"\" : value;\n\n/** A property and its four per-side longhands. */\nconst sideProps = (name: string): string[] => [name, ...SIDES.map((side) => `${name}-${side}`)];\n\n/** \"24px\" or \"12px 16px\": the sides of margin, padding or border, shortest form. */\nfunction sidesText(cs: CSSStyleDeclaration, property: \"margin\" | \"padding\" | \"border\"): string {\n const sides = sidesOf(cs, property);\n return compactSides(SIDES.map((side) => `${sides[side]}px`));\n}\n\nfunction layoutRows(el: Element, cs: CSSStyleDeclaration, stylesOf: StyleLookup): Spec[] {\n const rect = el.getBoundingClientRect();\n const size = layoutSize(el, rect);\n const onBoard = boardPosition(el, rect);\n const isFlex = cs.display.includes(\"flex\");\n const isGrid = cs.display.includes(\"grid\");\n\n const rows: Spec[] = [\n {\n name: \"size\",\n value: `${size.width} × ${size.height}`,\n props: [\"width\", \"height\"],\n note: sizingIntent(el, cs, stylesOf(el)),\n },\n { name: \"on board\", value: onBoard?.text ?? \"\", note: onBoard?.board },\n { name: \"padding\", value: unless(sidesText(cs, \"padding\"), \"0px\"), props: sideProps(\"padding\") },\n { name: \"margin\", value: unless(sidesText(cs, \"margin\"), \"0px\"), props: sideProps(\"margin\") },\n ];\n if (isFlex) {\n const wrap = cs.flexWrap === \"nowrap\" ? \"\" : \" wrap\";\n rows.push(\n { name: \"layout\", value: `flex ${cs.flexDirection}${wrap}` },\n { name: \"align\", value: unless(cs.alignItems, \"normal\") },\n { name: \"justify\", value: unless(cs.justifyContent, \"normal\") },\n );\n }\n if (isGrid) {\n rows.push(\n { name: \"layout\", value: \"grid\" },\n { name: \"columns\", value: unless(cs.gridTemplateColumns, \"none\") },\n );\n }\n if (isFlex || isGrid) {\n rows.push({\n name: \"gap\",\n value: unless(cs.gap, \"normal\", \"0px\"),\n props: [\"gap\", \"row-gap\", \"column-gap\"],\n });\n }\n if (cs.position !== \"static\") {\n const insets = SIDES.filter((side) => cs.getPropertyValue(side) !== \"auto\").map(\n (side) => `${side} ${cs.getPropertyValue(side)}`,\n );\n rows.push({ name: \"position\", value: [cs.position, ...insets].join(\" · \") });\n }\n return rows;\n}\n\nfunction shapeRows(cs: CSSStyleDeclaration): Spec[] {\n const borderWidths = sidesText(cs, \"border\");\n const hasBorder = borderWidths !== \"0px\";\n const radius = compactSides([\n cs.borderTopLeftRadius,\n cs.borderTopRightRadius,\n cs.borderBottomRightRadius,\n cs.borderBottomLeftRadius,\n ]);\n return [\n {\n name: \"background\",\n value: unless(cs.backgroundColor, TRANSPARENT),\n color: true,\n props: [\"background-color\", \"background\"],\n },\n {\n name: \"gradient\",\n value: unless(cs.backgroundImage, \"none\"),\n props: [\"background-image\", \"background\"],\n },\n {\n name: \"border\",\n value: hasBorder ? `${borderWidths} ${cs.borderTopStyle}` : \"\",\n props: [\"border-width\", \"border\", ...sideProps(\"border\")],\n },\n {\n name: \"border color\",\n value: hasBorder ? cs.borderTopColor : \"\",\n color: true,\n props: [\"border-color\", \"border\", ...sideProps(\"border\"), \"border-top-color\"],\n },\n { name: \"radius\", value: unless(radius, \"0px\"), props: [\"border-radius\"] },\n { name: \"shadow\", value: unless(cs.boxShadow, \"none\"), props: [\"box-shadow\"] },\n { name: \"opacity\", value: unless(cs.opacity, \"1\"), props: [\"opacity\"] },\n ];\n}\n\nfunction textRows(el: Element, cs: CSSStyleDeclaration): Spec[] {\n const hasText =\n hasValue(el) ||\n Array.from(el.childNodes).some((node) => node.nodeType === Node.TEXT_NODE && node.nodeValue?.trim());\n if (!hasText) return [];\n const family = cs.fontFamily.split(\",\")[0].replaceAll('\"', \"\");\n return [\n {\n name: \"font\",\n value: `${family} ${cs.fontWeight} ${cs.fontSize}/${cs.lineHeight}`,\n props: [\"font\", \"font-family\", \"font-weight\", \"font-size\", \"line-height\"],\n },\n { name: \"color\", value: cs.color, color: true, props: [\"color\"] },\n { name: \"spacing\", value: unless(cs.letterSpacing, \"normal\", \"0px\"), props: [\"letter-spacing\"] },\n { name: \"transform\", value: unless(cs.textTransform, \"none\") },\n { name: \"align\", value: unless(cs.textAlign, \"start\", \"left\") },\n { name: \"decoration\", value: unless(cs.textDecorationLine, \"none\") },\n ];\n}\n\nfunction iconRows(el: Element, cs: CSSStyleDeclaration): Spec[] {\n if (!(el instanceof SVGElement)) return [];\n return [\n { name: \"fill\", value: unless(cs.fill, \"none\"), color: true, props: [\"fill\", \"color\"] },\n { name: \"stroke\", value: unless(cs.stroke, \"none\"), color: true, props: [\"stroke\", \"color\"] },\n {\n name: \"stroke width\",\n value: cs.stroke === \"none\" ? \"\" : cs.strokeWidth,\n props: [\"stroke-width\"],\n },\n ];\n}\n\nfunction motionRows(cs: CSSStyleDeclaration): Spec[] {\n return motionItems(cs).map((item) => ({\n name: item.property,\n value: `${item.duration} ${item.easing}`,\n }));\n}\n\nexport function specSections(\n el: Element,\n cs: CSSStyleDeclaration,\n stylesOf: StyleLookup,\n): SpecSection[] {\n return [\n { label: \"Layout\", rows: layoutRows(el, cs, stylesOf) },\n { label: \"Shape\", rows: shapeRows(cs) },\n { label: \"Text\", rows: textRows(el, cs) },\n { label: \"Icon\", rows: iconRows(el, cs) },\n { label: \"Motion\", rows: motionRows(cs) },\n ];\n}\n\nexport interface SpecDifference {\n name: string;\n /** Undefined when that element has no such spec. */\n before?: Spec;\n after?: Spec;\n}\n\n/** The specs that differ between a baseline element's sections and another's. */\nexport function compareSpecs(\n base: SpecSection[],\n other: SpecSection[],\n): { differences: SpecDifference[]; identical: number } {\n const flatten = (sections: SpecSection[]) =>\n new Map<string, Spec>(\n sections.flatMap((section) =>\n section.rows.map((spec): [string, Spec] => [`${section.label}/${spec.name}`, spec]),\n ),\n );\n const before = flatten(base);\n const after = flatten(other);\n const keys = [...new Set([...before.keys(), ...after.keys()])]\n // Two elements are never in the same place.\n .filter((key) => !key.endsWith(\"/on board\"))\n .filter((key) => before.get(key)?.value || after.get(key)?.value);\n const differs = (key: string) => (before.get(key)?.value ?? \"\") !== (after.get(key)?.value ?? \"\");\n const differences = keys.filter(differs).map((key) => ({\n name: key.split(\"/\")[1],\n before: before.get(key),\n after: after.get(key),\n }));\n return { differences, identical: keys.length - differences.length };\n}\n","/** Design tokens: the CSS variables a design declares its values with. */\nimport { forEachPageRule, type StyleLookup } from \"../page-styles\";\n\n/** Properties a child takes from its parent when it declares none. */\nconst INHERITED = new Set([\n \"color\",\n \"fill\",\n \"stroke\",\n \"font\",\n \"font-family\",\n \"font-size\",\n \"font-weight\",\n \"line-height\",\n \"letter-spacing\",\n]);\n\n/**\n * The tokens named in the declarations that set `props` on an element.\n * Text and icon colours usually come from an ancestor, so for inherited\n * properties the search walks up until some element declares one.\n */\nexport function declaredTokens(el: Element, props: string[], stylesOf: StyleLookup): string[] {\n const inherits = props.length > 0 && props.every((prop) => INHERITED.has(prop));\n for (let node: Element | null = el; node; node = inherits ? node.parentElement : null) {\n const styles = stylesOf(node);\n const values = props\n .map((prop) => styles.findLast((style) => style.getPropertyValue(prop))?.getPropertyValue(prop))\n .filter((value) => value !== undefined);\n if (!values.length) continue;\n const names = values.flatMap((value) => [...value.matchAll(/var\\(\\s*(--[\\w-]+)/g)]);\n return [...new Set(names.map((match) => match[1]))];\n }\n return [];\n}\n\n/**\n * The token's path in the theme object its variable mirrors, for variables\n * that start with one of `prefixes`: with \"--ds-\",\n * \"--ds-color-text-primary\" -> \"color.text.primary\". \"\" for other tokens.\n */\nexport function tokenPath(token: string, prefixes: string[]): string {\n const prefix = prefixes.find((candidate) => token.startsWith(candidate));\n return prefix ? token.slice(prefix.length).replaceAll(\"-\", \".\") : \"\";\n}\n\nconst modesByToken = new Map<string, [scope: string, value: string][]>();\n\n/**\n * The token's value in every scope that sets it, for example \":root, .light\"\n * and \".dark\". Empty when the token has the same value everywhere.\n */\nexport function tokenModes(token: string): [scope: string, value: string][] {\n let modes = modesByToken.get(token);\n if (!modes) {\n const byScope = new Map<string, string>();\n forEachPageRule((rule) => {\n const value = rule.style.getPropertyValue(token).trim();\n if (value) byScope.set(rule.selectorText, value);\n });\n modes = new Set(byScope.values()).size > 1 ? [...byScope] : [];\n modesByToken.set(token, modes);\n }\n return modes;\n}\n\n/** True when the page defines any CSS variable the element can see. */\nexport const hasTokens = (cs: CSSStyleDeclaration): boolean =>\n Array.from(cs).some((prop) => prop.startsWith(\"--\"));\n","/** Click-to-copy for values, tokens and buttons in the panel. */\n\nconst FEEDBACK_MS = 1200;\n\n/**\n * Makes `node` copy `text` when clicked. For 1.2 seconds afterwards `node`,\n * and `feedbackOn` when given, carry the class `is-copied` (or\n * `is-copy-blocked`); the stylesheets turn that into a tint and a \"Copied\"\n * mark that add no size, so nothing around the node moves.\n */\nexport function copyOnClick<T extends HTMLElement>(\n node: T,\n text: string | (() => string),\n feedbackOn?: HTMLElement,\n): T {\n node.classList.add(\"copy\");\n node.title ||= \"Click to copy\";\n node.addEventListener(\"click\", async (e) => {\n e.stopPropagation();\n let result = \"is-copied\";\n try {\n await navigator.clipboard.writeText(typeof text === \"function\" ? text() : text);\n } catch {\n result = \"is-copy-blocked\";\n }\n const marked = feedbackOn ? [node, feedbackOn] : [node];\n marked.forEach((el) => el.classList.add(result));\n setTimeout(() => marked.forEach((el) => el.classList.remove(result)), FEEDBACK_MS);\n });\n return node;\n}\n","/**\n * The Specs tab: what a developer needs to build the pinned element.\n *\n * From top to bottom: the element header (where it is, what it is, its\n * classes), then one block per subject: comparison, component, layout, box,\n * colours, typography, motion. A click on any value, token or name copies\n * it.\n *\n * The action bar above switches the unit, shows a grid, compares with\n * another element, copies a link to the element and picks a colour.\n */\nimport { boardPosition } from \"../board\";\nimport { config } from \"../config\";\nimport { SIDES, type Sides, sidesOf } from \"../box-sides\";\nimport { h, layoutSize } from \"../dom\";\nimport { elementName, sizeText } from \"../element-name\";\nimport { easingGlyph, icon, type Intent, intentGlyph } from \"../icons\";\nimport { elementLink } from \"../link\";\nimport { options } from \"../options\";\nimport { createStyleLookup, type StyleLookup } from \"../page-styles\";\nimport { pin, pinnedElement, rerender } from \"../selection\";\nimport { formatColor, isColorValue, tokensWithColor } from \"../specs/colors\";\nimport { componentOf, componentProps } from \"../specs/component\";\nimport { motionItems } from \"../specs/motion\";\nimport { compareSpecs, type Spec, type SpecSection, specSections } from \"../specs/sections\";\nimport { declaredTokens, hasTokens, tokenModes, tokenPath } from \"../specs/tokens\";\nimport { copiedLength, copiedSpacing, shownLength } from \"../specs/units\";\nimport { copyOnClick } from \"./copy\";\nimport { saveLayout } from \"./layout\";\nimport { part, type Tab, tabBody } from \"./tab\";\n\nlet header: HTMLElement;\nlet body: HTMLElement;\n/** Whether the class list in the header shows every class. */\nlet classesExpanded = false;\n\n/** Everything a block needs to know about the element it describes. */\ninterface Context {\n el: Element;\n cs: CSSStyleDeclaration;\n stylesOf: StyleLookup;\n sections: SpecSection[];\n}\n\n// ── small builders ─────────────────────────────────────────────\n\nconst rootFontSize = (): number =>\n parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;\n\n/** A length as the tab shows it: a bare number in px, or rem. */\nconst shown = (value: string): string => shownLength(value, options.unit, rootFontSize());\n\n/** What a click on that length copies: with its unit, or bare, as the page configured. */\nconst copied = (value: string): string =>\n copiedLength(value, options.unit, rootFontSize(), config.copyFormat);\n\nconst hundredth = (pixels: number): number => Math.round(pixels * 100) / 100;\n\n/** A spec's value as text: colours as hex, lengths in the chosen unit. */\nconst shownSpec = (spec: Spec): string =>\n spec.color ? formatColor(spec.value) : shown(spec.value);\n\nfunction swatch(color: string, small = false): HTMLElement {\n const chip = h(\"span\", small ? \"swatch is-small\" : \"swatch\");\n chip.style.setProperty(\"--swatch\", color);\n return chip;\n}\n\n/** A titled block of the tab; `note` sits at the right of the title. */\nfunction block(title: string, note = \"\", className = \"\"): HTMLElement {\n const section = body.appendChild(h(\"section\", `block ${className}`));\n const head = section.appendChild(h(\"div\", \"block-head\"));\n head.appendChild(h(\"h3\", \"\", title));\n if (note) head.appendChild(h(\"span\", \"note\", note));\n return section;\n}\n\n/** A label with a value that copies on click. `wide` lets a long value wrap. */\nfunction cell(label: string, value: string, wide = false): HTMLElement {\n const row = h(\"div\", wide ? \"wide\" : \"cell\");\n row.appendChild(h(\"span\", \"label\", label));\n copyOnClick(row.appendChild(h(\"span\", \"value\", shown(value))), copied(value));\n return row;\n}\n\n/** A spec by section and name, when it has a value. */\nconst findSpec = ({ sections }: Context, section: string, name: string): Spec | undefined =>\n sections.find((s) => s.label === section)?.rows.find((row) => row.name === name && row.value);\n\n// ── element header ─────────────────────────────────────────────\n\n/** Class names a build tool made up (\"sc-cOPLXt\", \"krMpoe\", \"MuiBox-root-ifjwsH\"). */\nfunction looksGenerated(className: string): boolean {\n const hash = /(?:^|-)([A-Za-z]{6})$/.exec(className)?.[1] ?? \"\";\n const mixedCase = /[a-z]/.test(hash) && /[A-Z]/.test(hash);\n return /^(sc|css|jss)-/.test(className) || mixedCase;\n}\n\nfunction renderHeader(el: Element): void {\n const rect = el.getBoundingClientRect();\n const board = boardPosition(el, rect)?.board;\n const parent = el.parentElement;\n const classes = Array.from(el.classList);\n const meaningful = classes.filter((name) => !looksGenerated(name));\n header.replaceChildren();\n\n // Where it is: board › parent › element\n const crumbs = header.appendChild(h(\"div\", \"crumbs\"));\n const separator = () => crumbs.appendChild(h(\"span\", \"sep\", \"›\")).setAttribute(\"aria-hidden\", \"true\");\n if (board) {\n crumbs.appendChild(h(\"span\", \"crumb\", board));\n separator();\n }\n if (parent && parent !== document.body) {\n const up = crumbs.appendChild(h(\"button\", \"crumb\", elementName(parent)));\n up.title = \"Pin the parent\";\n up.addEventListener(\"click\", () => pin(parent));\n separator();\n }\n crumbs.appendChild(h(\"span\", \"crumb is-current\", elementName(el)));\n\n // What it is: tag, name, size\n const title = header.appendChild(h(\"div\", \"title-row\"));\n title.appendChild(h(\"span\", \"tag-chip\", el.localName));\n const name = elementName(el);\n title.appendChild(h(\"span\", \"title\", name === el.localName ? (meaningful[0] ?? name) : name));\n title.appendChild(h(\"span\", \"spacer\"));\n title.appendChild(h(\"span\", \"title-size\", sizeText(el, rect)));\n\n // Its classes: the meaningful ones on one line, or every class as a chip\n if (!classes.length) return;\n const toggle = h(\"button\", \"more\");\n toggle.addEventListener(\"click\", () => {\n classesExpanded = !classesExpanded;\n renderHeader(el);\n });\n if (classesExpanded) {\n const list = header.appendChild(h(\"div\", \"class-list\"));\n [...meaningful, ...classes.filter(looksGenerated)].forEach((className) => {\n const chip = h(\"span\", \"class-chip\", \".\" + className);\n chip.classList.toggle(\"is-generated\", looksGenerated(className));\n list.appendChild(copyOnClick(chip, className));\n });\n toggle.append(\"Less\", icon(\"chevronUp\", 10));\n list.appendChild(toggle);\n } else {\n const row = header.appendChild(h(\"div\", \"class-row\"));\n const visible = meaningful.length ? meaningful : classes;\n row.appendChild(h(\"span\", \"classes\", visible.map((className) => \".\" + className).join(\" \")));\n const hidden = classes.length - visible.length;\n // One or two plain classes fit on the line: nothing more to show.\n if (hidden || classes.length > 2) {\n toggle.append(hidden ? `+${hidden}` : \"All\", icon(\"chevronDown\", 10));\n row.appendChild(toggle);\n }\n }\n}\n\n// ── component ──────────────────────────────────────────────────\n\nfunction renderComponent({ el }: Context): void {\n const component = componentOf(el);\n if (!component) return;\n const section = block(\n component.inside ? \"Inside component\" : \"Component\",\n component.inside ? \"Contains this element\" : \"This element\",\n \"component\",\n );\n\n // The name, in a box that copies it.\n const name = h(\"div\", component.inside ? \"code is-inside\" : \"code\");\n name.appendChild(h(\"span\", \"name\", component.name));\n const mark = name.appendChild(h(\"span\", \"copy-mark\"));\n mark.append(icon(\"copy\"), icon(\"check\"));\n mark.firstElementChild!.classList.add(\"when-idle\");\n mark.lastElementChild!.classList.add(\"when-copied\");\n section.appendChild(copyOnClick(name, component.name));\n\n // The props the design passes. Not a suggestion of code: the inspector\n // does not know which props the project's own component accepts.\n const props = componentProps(component);\n if (props.length) {\n section.appendChild(h(\"div\", \"hint\", \"Props in this design\"));\n const list = section.appendChild(h(\"div\", \"props\"));\n for (const prop of props) {\n list.appendChild(h(\"span\", \"prop-name\", prop.name));\n list.appendChild(copyOnClick(h(\"span\", \"value\", prop.value), prop.value));\n }\n }\n\n if (component.sourcePath) {\n // The path may wrap at slashes, never inside a name.\n const folders = component.sourcePath.split(\"/\");\n const file = folders.pop() ?? \"\";\n const source = section.appendChild(h(\"div\", \"source\"));\n source.appendChild(h(\"span\", \"label\", \"Source\"));\n const path = source.appendChild(h(\"span\", \"path\"));\n folders.forEach((folder) => path.append(folder + \"/\", document.createElement(\"wbr\")));\n path.appendChild(h(\"span\", \"file\", file));\n copyOnClick(path, component.sourcePath);\n }\n\n const root = component.element;\n if (component.inside && root) {\n const row = section.appendChild(h(\"div\", \"select-row\"));\n const select = row.appendChild(h(\"button\", \"ghost-button\"));\n select.append(icon(\"chevronUp\", 12), \"Select component\");\n select.addEventListener(\"click\", () => pin(root));\n row.appendChild(h(\"span\", \"hint\", `Pinned: ${el.localName} inside it`));\n }\n}\n\n// ── compare ────────────────────────────────────────────────────\n\nfunction stopComparing(): void {\n options.compareWith = null;\n rerender();\n}\n\n/** One side of a difference: a swatch for colours, then the value. */\nfunction compareSide(spec: Spec | undefined, className: string): HTMLElement {\n const side = h(\"span\", `side ${className}`);\n if (!spec?.value) {\n side.textContent = \"none\";\n } else {\n if (spec.color) side.appendChild(swatch(spec.value, true));\n side.append(shownSpec(spec));\n }\n return side;\n}\n\n/** A is the baseline (pinned when compare started); B is the element pinned now. */\nfunction renderCompare(context: Context): void {\n const baseline = options.compareWith;\n if (!baseline?.isConnected) return;\n const section = block(\"Compare\");\n const stop = section.firstElementChild!.appendChild(h(\"button\", \"ghost-button\"));\n stop.append(icon(\"close\", 12), \"Stop\", h(\"kbd\", \"\", \"Esc\"));\n stop.addEventListener(\"click\", stopComparing);\n\n const who = (letter: string, el: Element, role: string) => {\n const row = section.appendChild(h(\"div\", \"who\"));\n row.appendChild(h(\"span\", letter === \"A\" ? \"letter\" : \"letter is-b\", letter));\n row.appendChild(h(\"span\", \"name\", elementName(el)));\n row.appendChild(h(\"span\", \"board\", boardPosition(el, el.getBoundingClientRect())?.board ?? \"\"));\n row.appendChild(h(\"span\", \"spacer\"));\n row.appendChild(h(\"span\", \"hint\", role));\n };\n who(\"A\", baseline, \"Baseline\");\n if (baseline === context.el) {\n section.appendChild(h(\"div\", \"compare-summary\", \"Pin another element to compare it with A.\"));\n return;\n }\n who(\"B\", context.el, \"Pinned\");\n\n const baselineSections = specSections(baseline, getComputedStyle(baseline), context.stylesOf);\n const { differences, identical } = compareSpecs(baselineSections, context.sections);\n const count = differences.length === 1 ? \"1 difference\" : `${differences.length} differences`;\n section.appendChild(h(\"div\", \"compare-summary\", `${count} · ${identical} identical`));\n for (const difference of differences) {\n const row = section.appendChild(h(\"div\", \"compare-row\"));\n row.appendChild(h(\"span\", \"label\", difference.name));\n row.append(\n compareSide(difference.before, \"is-before\"),\n icon(\"arrow\"),\n compareSide(difference.after, \"is-after\"),\n );\n }\n}\n\n// ── layout ─────────────────────────────────────────────────────\n\nfunction sizeRow(el: Element, intents: string): HTMLElement {\n const row = h(\"div\", \"size-row\");\n row.appendChild(h(\"span\", \"label\", \"Size\"));\n const size = sizeText(el);\n copyOnClick(row.appendChild(h(\"span\", \"value\", size)), size);\n row.appendChild(h(\"span\", \"spacer\"));\n\n // \"fill × hug\": each word with its glyph; the height glyph is turned 90°.\n const [width, height] = intents.split(\" × \") as Intent[];\n const list = row.appendChild(h(\"span\", \"intents\"));\n const intent = (word: Intent, className: string) => {\n const item = list.appendChild(h(\"span\", `intent ${className}`));\n item.append(intentGlyph(word), word);\n };\n intent(width, \"is-width\");\n list.appendChild(h(\"span\", \"\", \"×\"));\n intent(height, \"is-height\");\n return row;\n}\n\n/** Tokens a non-colour value is declared with, as \"Gap --spacing-4\" lines. */\nfunction tokenNotes(context: Context, specs: [label: string, spec: Spec | undefined][]): HTMLElement[] {\n return specs.flatMap(([label, spec]) =>\n declaredTokens(context.el, spec?.props ?? [], context.stylesOf).map((token) => {\n const row = h(\"div\", \"wide\");\n row.appendChild(h(\"span\", \"label\", label));\n const path = tokenPath(token, config.tokenPrefixes);\n copyOnClick(row.appendChild(h(\"span\", \"token-note\", path || token)), path || token);\n return row;\n }),\n );\n}\n\nfunction renderLayout(context: Context): void {\n const { el, cs } = context;\n const onBoard = boardPosition(el, el.getBoundingClientRect());\n const section = block(\"Layout\", onBoard ? \"X, Y on board\" : \"\");\n section.appendChild(sizeRow(el, findSpec(context, \"Layout\", \"size\")?.note ?? \"hug × hug\"));\n\n const spec = (sectionName: string, name: string) => findSpec(context, sectionName, name);\n const isFlex = cs.display.includes(\"flex\");\n const cells: [label: string, value: string | undefined, spec?: Spec][] = [\n [\"X\", onBoard ? String(onBoard.x) : \"\"],\n [\"Y\", onBoard ? String(onBoard.y) : \"\"],\n [\"Display\", cs.display],\n [\"Direction\", isFlex ? cs.flexDirection + (cs.flexWrap === \"nowrap\" ? \"\" : \" wrap\") : \"\"],\n [\"Align\", spec(\"Layout\", \"align\")?.value],\n [\"Justify\", spec(\"Layout\", \"justify\")?.value],\n [\"Gap\", spec(\"Layout\", \"gap\")?.value, spec(\"Layout\", \"gap\")],\n [\"Radius\", spec(\"Shape\", \"radius\")?.value, spec(\"Shape\", \"radius\")],\n [\"Border\", spec(\"Shape\", \"border\")?.value, spec(\"Shape\", \"border\")],\n [\"Opacity\", spec(\"Shape\", \"opacity\")?.value, spec(\"Shape\", \"opacity\")],\n [\"Stroke\", spec(\"Icon\", \"stroke width\")?.value, spec(\"Icon\", \"stroke width\")],\n ];\n const grid = section.appendChild(h(\"div\", \"grid2\"));\n cells.forEach(([label, value]) => value && grid.appendChild(cell(label, value)));\n\n // Values too long for half a row\n const wide: [label: string, spec: Spec | undefined][] = [\n [\"Position\", spec(\"Layout\", \"position\")],\n [\"Columns\", spec(\"Layout\", \"columns\")],\n [\"Shadow\", spec(\"Shape\", \"shadow\")],\n [\"Gradient\", spec(\"Shape\", \"gradient\")],\n ];\n wide.forEach(([label, row]) => row && section.appendChild(cell(label, row.value, true)));\n\n const tokenised = [\n ...cells.map(([label, , row]): [string, Spec | undefined] => [label, row]),\n ...wide,\n ];\n section.append(...tokenNotes(context, tokenised));\n}\n\n// ── box ────────────────────────────────────────────────────────\n\n/** The four numbers of one ring, each on its own side; a click copies it. */\nfunction boxSides(box: HTMLElement, sides: Sides): void {\n for (const side of SIDES) {\n const value = `${hundredth(sides[side])}px`;\n box.appendChild(copyOnClick(h(\"span\", `box-side is-${side}`, shown(value)), copied(value)));\n }\n}\n\n/** Margin around padding around the content size, as nested boxes. */\nfunction renderBox({ el, cs }: Context): void {\n const margin = sidesOf(cs, \"margin\");\n const padding = sidesOf(cs, \"padding\");\n const border = sidesOf(cs, \"border\");\n const size = layoutSize(el, el.getBoundingClientRect());\n const contentWidth = size.width - padding.left - padding.right - border.left - border.right;\n const contentHeight = size.height - padding.top - padding.bottom - border.top - border.bottom;\n const spacing = (name: \"margin\" | \"padding\", sides: Sides) =>\n copiedSpacing(\n name,\n SIDES.map((side) => hundredth(sides[side])),\n options.unit,\n rootFontSize(),\n config.copyFormat,\n );\n\n const section = block(\"Box\");\n const marginBox = section.appendChild(h(\"div\", \"box-margin\"));\n const marginName = copyOnClick(h(\"span\", \"box-name\", \"margin\"), () => spacing(\"margin\", margin));\n marginName.title = \"Click to copy all four sides\";\n marginBox.appendChild(marginName);\n boxSides(marginBox, margin);\n\n const paddingBox = marginBox.appendChild(h(\"div\", \"box-padding\"));\n const paddingName = copyOnClick(h(\"span\", \"box-name\", \"padding\"), () =>\n spacing(\"padding\", padding),\n );\n paddingName.title = \"Click to copy all four sides\";\n paddingBox.appendChild(paddingName);\n boxSides(paddingBox, padding);\n\n const content = `${hundredth(contentWidth)} × ${hundredth(contentHeight)}`;\n paddingBox.appendChild(h(\"div\", \"box-content\", content));\n}\n\n// ── colours ────────────────────────────────────────────────────\n\n/** \":root, .light\" -> \"light\"; \".dark\" -> \"dark\"; \":root\" -> \"default\". */\nfunction scopeName(selector: string): string {\n const name = /([\\w-]+)[\"'\\]]*\\s*$/.exec(selector)?.[1] ?? selector;\n return name === \"root\" ? \"default\" : name;\n}\n\n/**\n * A colour with its design token, in the order a developer reads it: the\n * resolved value, the theme path (what goes into `sx`), the CSS variable,\n * then the value in each theme. Every line copies on its own.\n */\nfunction colorRow(spec: Spec, role: string, context: Context): HTMLElement {\n const { el, cs, stylesOf } = context;\n const row = h(\"div\", \"token-row\");\n const copies = <T extends HTMLElement>(node: T, text: string) => copyOnClick(node, text, row);\n const [hex, alpha = \"100%\"] = formatColor(spec.value).split(\" \");\n\n const line = row.appendChild(h(\"div\", \"token-line\"));\n line.appendChild(swatch(spec.value));\n line.appendChild(copies(h(\"span\", \"value\", hex), hex));\n if (isColorValue(hex)) line.appendChild(h(\"span\", \"alpha\", alpha));\n\n const declared = declaredTokens(el, spec.props ?? [], stylesOf);\n const similar = declared.length || !hasTokens(cs) ? [] : tokensWithColor(cs, spec.value);\n const missing = !declared.length && !similar.length && hasTokens(cs);\n if (similar.length) line.appendChild(h(\"span\", \"badge\", \"Matches token\"));\n if (missing) {\n const badge = line.appendChild(h(\"span\", \"badge is-warn\"));\n badge.append(icon(\"warning\", 10), \"No token\");\n row.classList.add(\"is-missing\");\n }\n\n line.appendChild(h(\"span\", \"spacer\"));\n const slot = line.appendChild(h(\"span\", \"role\"));\n slot.appendChild(h(\"span\", \"name\", role));\n slot.appendChild(h(\"span\", \"copy-hint\")).append(icon(\"copy\", 12), \"Copy\");\n slot.appendChild(h(\"span\", \"copied\")).append(icon(\"check\", 12), \"Copied\");\n\n for (const token of declared) {\n const path = tokenPath(token, config.tokenPrefixes);\n if (path) row.appendChild(copies(h(\"div\", \"theme-path\", path), path));\n row.appendChild(copies(h(\"div\", path ? \"css-var is-secondary\" : \"css-var\", token), token));\n const modes = tokenModes(token);\n if (modes.length) row.appendChild(modesLine(modes, cs.getPropertyValue(token).trim()));\n }\n // A colour the design wrote by hand that happens to equal a token.\n similar.slice(0, 2).forEach((token) => {\n row.appendChild(copies(h(\"div\", \"css-var is-secondary\", token), token));\n });\n if (missing) row.appendChild(h(\"div\", \"warn-text\", \"Hard-coded value. No token has this colour.\"));\n return row;\n}\n\n/** The token's value in each theme; the theme the page is using stands out. */\nfunction modesLine(modes: [scope: string, value: string][], current: string): HTMLElement {\n const line = h(\"div\", \"modes\");\n for (const [scope, value] of modes) {\n const mode = line.appendChild(h(\"span\", value === current ? \"mode is-active\" : \"mode\"));\n if (isColorValue(value)) mode.appendChild(swatch(value, true));\n mode.append(value, h(\"span\", \"scope\", scopeName(scope)));\n copyOnClick(mode, value);\n }\n return line;\n}\n\nconst COLOR_ROLES: [section: string, name: string, role: string][] = [\n [\"Shape\", \"background\", \"Background\"],\n [\"Shape\", \"border color\", \"Border\"],\n [\"Text\", \"color\", \"Text\"],\n [\"Icon\", \"fill\", \"Fill\"],\n [\"Icon\", \"stroke\", \"Stroke\"],\n];\n\nfunction renderColors(context: Context): void {\n const rows = COLOR_ROLES.flatMap(([section, name, role]) => {\n const spec = findSpec(context, section, name);\n return spec ? [colorRow(spec, role, context)] : [];\n });\n if (options.pickedColor) {\n rows.push(colorRow({ name: \"picked\", value: options.pickedColor, color: true }, \"Picked\", context));\n }\n if (!rows.length) return;\n block(\"Colors\").appendChild(h(\"div\", \"token-rows\")).append(...rows);\n}\n\n// ── typography ─────────────────────────────────────────────────\n\nfunction renderTypography(context: Context): void {\n const { cs } = context;\n const font = findSpec(context, \"Text\", \"font\");\n if (!font) return;\n const section = block(\"Typography\");\n const family = cs.fontFamily.split(\",\")[0].replaceAll('\"', \"\");\n section.appendChild(cell(\"Family\", family));\n\n const text = (name: string) => findSpec(context, \"Text\", name)?.value;\n const cells: [label: string, value: string | undefined][] = [\n [\"Weight\", cs.fontWeight],\n [\"Size\", cs.fontSize],\n [\"Line\", cs.lineHeight],\n [\"Tracking\", text(\"spacing\")],\n [\"Transform\", text(\"transform\")],\n [\"Align\", text(\"align\")],\n [\"Decoration\", text(\"decoration\")],\n ];\n const grid = section.appendChild(h(\"div\", \"grid2\"));\n cells.forEach(([label, value]) => value && grid.appendChild(cell(label, value)));\n section.append(...tokenNotes(context, [[\"Font\", font]]));\n}\n\n// ── motion ─────────────────────────────────────────────────────\n\nfunction renderMotion({ cs }: Context): void {\n const items = motionItems(cs);\n if (!items.length) return;\n const section = block(\"Motion\");\n for (const item of items) {\n const row = section.appendChild(h(\"div\", \"motion-row\"));\n row.appendChild(easingGlyph(item.curve));\n row.appendChild(h(\"span\", \"property\", item.property));\n row.appendChild(h(\"span\", \"duration\", item.duration));\n row.appendChild(h(\"span\", \"easing\", item.easing));\n row.title = \"Click to copy\";\n copyOnClick(row, `${item.property} ${item.duration} ${item.easing}`);\n }\n}\n\n// ── action bar ─────────────────────────────────────────────────\n\n/** A row of exclusive choices; marks the one equal to `current()`. */\nfunction segmented(group: HTMLElement, current: () => string, choose: (value: string) => void) {\n const segments = Array.from(group.querySelectorAll<HTMLElement>(\".segment\"));\n const show = () => {\n segments.forEach((segment) => {\n segment.setAttribute(\"aria-checked\", String(segment.dataset.value === current()));\n });\n group.classList.toggle(\"is-on\", group.querySelector('.is-on[aria-checked=\"true\"]') !== null);\n };\n segments.forEach((segment) => {\n segment.addEventListener(\"click\", () => {\n choose(segment.dataset.value ?? \"\");\n show();\n rerender();\n });\n });\n show();\n}\n\nfunction mountActionBar(panel: HTMLElement): void {\n segmented(\n part(panel, '[data-option=\"unit\"]'),\n () => options.unit,\n (value) => {\n options.unit = value === \"rem\" ? \"rem\" : \"px\";\n saveLayout(panel);\n },\n );\n segmented(\n part(panel, '[data-option=\"grid\"]'),\n () => String(options.gridStep),\n (value) => {\n options.gridStep = Number(value);\n },\n );\n\n const compare = part(panel, '[data-action=\"compare\"]');\n const showCompare = () => compare.setAttribute(\"aria-pressed\", String(options.compareWith !== null));\n compare.addEventListener(\"click\", () => {\n options.compareWith = options.compareWith ? null : pinnedElement();\n rerender();\n });\n // Esc ends a comparison, wherever the focus is on the page.\n document.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\" && options.compareWith) stopComparing();\n });\n\n const pick = part(panel, '[data-action=\"pick\"]');\n pick.hidden = !window.EyeDropper;\n pick.addEventListener(\"click\", async () => {\n pick.setAttribute(\"aria-pressed\", \"true\");\n try {\n options.pickedColor = (await new window.EyeDropper!().open()).sRGBHex;\n } catch {\n // The user pressed Escape.\n }\n pick.setAttribute(\"aria-pressed\", \"false\");\n rerender();\n });\n\n copyOnClick(part(panel, '[data-action=\"link\"]'), () => {\n const pinned = pinnedElement();\n return pinned ? elementLink(pinned) : location.href;\n });\n\n renderActionStates = showCompare;\n}\n\n/** Brings the action bar in line with the options; set once the bar is mounted. */\nlet renderActionStates = (): void => {};\n\n// ── tab ────────────────────────────────────────────────────────\n\nfunction renderSpecs(el: Element): void {\n const cs = getComputedStyle(el);\n const stylesOf = createStyleLookup();\n const context: Context = { el, cs, stylesOf, sections: specSections(el, cs, stylesOf) };\n\n renderActionStates();\n renderHeader(el);\n const scrollTop = body.scrollTop;\n body.replaceChildren();\n renderCompare(context);\n renderComponent(context);\n renderLayout(context);\n renderBox(context);\n renderColors(context);\n renderTypography(context);\n renderMotion(context);\n body.scrollTop = scrollTop;\n}\n\nexport const specsTab: Tab = {\n mount(panel) {\n header = part(panel, \".element-header\");\n body = tabBody(panel, \"specs\");\n mountActionBar(panel);\n },\n render(el, change) {\n if (change === \"pinned\") body.scrollTop = 0;\n renderSpecs(el);\n },\n};\n","/**\n * The side panel: docked to the right edge, with a tab bar, a » menu (the\n * Reference tab and \"Collapse panel\") and a resize handle. It appears when\n * an element is pinned.\n */\nimport { h } from \"../dom\";\nimport { elementName } from \"../element-name\";\nimport { mount, setRootVariable } from \"../root\";\nimport { onSelection, unpin } from \"../selection\";\nimport { changesTab } from \"./changes-tab\";\nimport { cssTab } from \"./css-tab\";\nimport { layersTab } from \"./layers-tab\";\nimport { loadLayout, saveLayout, setTab } from \"./layout\";\nimport template from \"./panel.html?raw\";\nimport { referenceTab } from \"./reference-tab\";\nimport { specsTab } from \"./specs-tab\";\nimport { part, type Tab } from \"./tab\";\n\nconst TABS: Tab[] = [specsTab, cssTab, layersTab, changesTab, referenceTab];\nconst MIN_WIDTH = 320;\nconst MAX_WIDTH = 560;\n\nlet panel: HTMLElement | null = null;\n\n/** Tells the rest of the inspector how much of the right edge the panel covers. */\nfunction reportDock(): void {\n const visible = panel !== null && panel.style.display !== \"none\";\n setRootVariable(\"--ins-dock\", `${visible ? panel!.offsetWidth : 0}px`);\n}\n\nfunction setCollapsed(el: HTMLElement, collapsed: boolean): void {\n el.classList.toggle(\"is-collapsed\", collapsed);\n reportDock();\n}\n\n/** Dragging the panel's left edge changes its width. */\nfunction resizeByDragging(el: HTMLElement): void {\n part(el, \".resize\").addEventListener(\"mousedown\", (down) => {\n down.preventDefault();\n const startWidth = el.offsetWidth;\n const move = (e: MouseEvent) => {\n const width = startWidth + down.clientX - e.clientX;\n el.style.width = `${Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, width))}px`;\n reportDock();\n };\n const stop = () => {\n document.removeEventListener(\"mousemove\", move);\n document.removeEventListener(\"mouseup\", stop);\n saveLayout(el);\n };\n document.addEventListener(\"mousemove\", move);\n document.addEventListener(\"mouseup\", stop);\n });\n}\n\n/** The » button opens a small menu: the Reference tab and \"Collapse panel\". */\nfunction mountMoreMenu(el: HTMLElement): void {\n const button = part(el, \".more-button\");\n const menu = part(el, \".more-menu\");\n const setOpen = (open: boolean) => {\n menu.hidden = !open;\n button.setAttribute(\"aria-expanded\", String(open));\n };\n button.addEventListener(\"click\", (e) => {\n e.stopPropagation();\n setOpen(menu.hidden);\n });\n // Any other click in the panel, or Esc, closes the menu.\n el.addEventListener(\"click\", () => setOpen(false));\n el.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Escape\") setOpen(false);\n });\n part(menu, '[data-tab=\"reference\"]').addEventListener(\"click\", () => setTab(el, \"reference\"));\n part(menu, \".collapse\").addEventListener(\"click\", () => setCollapsed(el, true));\n}\n\nfunction createPanel(): HTMLElement {\n const el = mount(h(\"div\", \"panel\"));\n el.innerHTML = template;\n loadLayout(el);\n\n // Keys typed in the panel must not trigger the page's own shortcuts.\n el.addEventListener(\"keydown\", (e) => e.stopPropagation());\n part(el, \".tabs\").addEventListener(\"click\", (e) => {\n const tab = (e.target as Element).closest<HTMLElement>(\".tab\")?.dataset.tab;\n if (tab) setTab(el, tab);\n });\n part(el, \".close\").addEventListener(\"click\", unpin);\n mountMoreMenu(el);\n part(el, \".rail\").addEventListener(\"click\", () => setCollapsed(el, false));\n resizeByDragging(el);\n\n TABS.forEach((tab) => tab.mount(el));\n return el;\n}\n\nexport function startPanel(): void {\n onSelection((el, change) => {\n if (!el) {\n if (panel) panel.style.display = \"none\";\n reportDock();\n return;\n }\n panel ??= createPanel();\n panel.style.display = \"\";\n part(panel, \".rail .name\").textContent = `Inspect · ${elementName(el)}`;\n if (change === \"pinned\") setCollapsed(panel, false);\n TABS.forEach((tab) => tab.render(el, change));\n reportDock();\n });\n}\n","/**\n * Groma — a Figma-style inspector for any HTML page.\n *\n * Alt + hover box model of the hovered element, with a name chip\n * Alt + click pin the element: the side panel opens\n * Alt + hover while an element is pinned: distances from it\n * Alt + right-click list every layer under the pointer\n *\n * The launcher pill (bottom right) locks inspect mode on, so none of these\n * need Alt.\n *\n * This file only wires the features together. Each one lives in its own\n * module; start with overlay/hover.ts and panel/panel.ts.\n */\nimport { type Config, configure, configureFromScriptTag } from \"./config\";\nimport { replayOverrides } from \"./edits/overrides\";\nimport { startLauncher } from \"./launcher\";\nimport { pinFromLink } from \"./link\";\nimport { highlight, startOutlines } from \"./outlines\";\nimport { startHover } from \"./overlay/hover\";\nimport { startLayerMenu } from \"./overlay/layer-menu\";\nimport { startPanel } from \"./panel/panel\";\nimport { pin, pinnedElement } from \"./selection\";\n\n/** For pages that re-render and need to bring the inspector along. */\nexport interface GromaApi {\n /** Pins an element, as Alt+click does. */\n pin(el: Element): void;\n /** The element pinned in the panel, or null. */\n pinnedElement(): Element | null;\n /** Re-applies saved edits to the whole page, or only inside `root`. */\n replayOverrides(root?: ParentNode): void;\n /** Moves the pinned-element outline to `el`. */\n highlight(el: Element): void;\n /** Changes how values are copied and which tokens show a theme path (see config.ts). */\n configure(changes?: Partial<Config>): void;\n}\n\nfunction start(): void {\n configureFromScriptTag();\n startOutlines();\n startPanel();\n startHover();\n startLayerMenu();\n startLauncher();\n\n // Saved edits: now for a page that is already parsed, then again as the\n // page fills in. Pages that render later still call replayOverrides().\n replayOverrides();\n document.addEventListener(\"DOMContentLoaded\", () => replayOverrides());\n window.addEventListener(\"load\", () => replayOverrides());\n pinFromLink();\n\n window.Groma = { pin, pinnedElement, replayOverrides, highlight, configure };\n}\n\n// A page may include the script twice (a tag in the page and one injected).\nif (!window.Groma) start();\n"],"mappings":"C,WAaA,IAAIM,EAAyB,KACvBC,EAAwB,CAAC,EAElBC,MAAsCF,EAEnD,SAAgBG,EAAY,EAA0B,CACpDF,EAAU,KAAKG,CAAQ,CACzB,CAEA,SAASC,EAAO,EAA+B,CAC7CJ,EAAU,QAAS,GAAaG,EAASJ,EAAQM,CAAM,CAAC,CAC1D,CAEA,SAAgBC,EAAI,EAAmB,CACrC,EAASC,EACTH,EAAO,QAAQ,CACjB,CAEA,SAAgBI,GAAc,CAC5B,EAAS,KACTJ,EAAO,QAAQ,CACjB,CAGA,SAAgBK,GAAiB,CAC/BL,EAAO,SAAS,CAClB,CCTA,IAAMM,EAAmB,CAAE,WAAY,MAAO,cAAe,CAAC,CAAE,EAEnDC,EAAiB,CAAE,GAAGD,CAAS,EAG5C,SAAgBE,EAAU,EAA2BF,EAAgB,EAC/DG,EAAQ,aAAe,OAASA,EAAQ,aAAe,QACzD,EAAO,WAAaA,EAAQ,YAE1B,MAAM,QAAQA,EAAQ,aAAa,IACrC,EAAO,cAAgBA,EAAQ,cAAc,OAAQ,GAAWC,EAAO,WAAW,IAAI,CAAC,GAEzFL,EAAS,CACX,CAGA,SAAgBM,GAA+B,CAE7C,IAAMC,EAAM,SAAS,eAAiB,SAAS,cAAc,oBAAoB,EAC3EA,aAAe,aACrBJ,EAAU,CACR,WAAYI,EAAI,QAAQ,KACxB,cAAeA,EAAI,QAAQ,aAAa,MAAM,QAAQ,CAAC,CAAC,OAAO,OAAO,CACxE,CAAC,CACH,CCpDA,SAAgBC,EAA4B,EAAyB,CACnE,GAAI,CACF,OAAO,KAAK,MAAM,aAAa,QAAQC,CAAG,GAAK,IAAI,GAAK,CAAC,CAC3D,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAEA,SAAgBC,EAAW,EAAa,EAAqB,CAC3D,GAAI,CACF,aAAa,QAAQD,EAAK,KAAK,UAAUE,CAAK,CAAC,CACjD,MAAQ,CAER,CACF,CCNA,IAAMC,EAAY,qBAAuB,SAAS,SAC5CC,EAAc,gBAAkB,SAAS,SAMzCC,GAAiB,IAAI,QACrBC,GAAkB,IAAI,QAEtBC,GAAe,GAAsClB,EAAmB,MAI9E,SAAgBmB,GAAY,EAA0C,CACpE,OAAOT,EAA0BI,CAAS,CAAC,CAACM,IAAa,CAAC,CAC5D,CAGA,SAAgBC,GAAU,EAAkB,EAAc,EAA4B,CACpF,IAAMC,EAAMZ,EAA0BI,CAAS,EACzCS,EAAS,EAAIH,KAAc,CAAC,EAC9BP,IAAU,KAAM,OAAOU,EAAMC,GAC5B,EAAMA,GAAQX,EACd,OAAO,KAAKU,CAAK,CAAC,CAAC,QAAQ,OAAOD,EAAIF,GAC3CR,EAAWE,EAAWQ,CAAG,CAC3B,CAEA,SAAgBG,GAAW,EAAa,EAAc,EAAqB,CACzE,IAAMC,EAAQR,GAAYlB,CAAE,EACtB2B,EAAYX,GAAe,IAAIhB,CAAE,GAAK,CAAC,EAC7CgB,GAAe,IAAIhB,EAAI2B,CAAS,EAChC,EAAUH,KAAU,CAACE,EAAM,iBAAiBF,CAAI,EAAGE,EAAM,oBAAoBF,CAAI,CAAC,EAClFE,EAAM,YAAYF,EAAMX,CAAK,CAC/B,CAGA,SAAgBe,GAAa,EAAa,EAAoB,CAC5D,GAAM,CAACf,EAAOgB,GAAYb,GAAe,IAAIhB,CAAE,CAAC,GAAGwB,IAAS,CAAC,GAAI,EAAE,EACnEN,GAAYlB,CAAE,CAAC,CAAC,YAAYwB,EAAMX,EAAOgB,CAAQ,CACnD,CAIA,IAAMC,GAAa,IAAI,IAAI,CAAC,QAAS,WAAY,SAAU,QAAQ,CAAC,EAGvDC,EAAY,GAAyBD,GAAW,IAAI9B,EAAG,SAAS,EAE7E,SAAgBgC,GAAW,EAAqB,CAC9C,OAAOD,EAAS/B,CAAE,EAAKA,EAAwB,MAAQA,EAAG,SAC5D,CAEA,SAASiC,GAAW,EAAa,EAAuB,CAClDF,EAAS/B,CAAE,EAAG,EAAyB,MAAQkC,EAC9C,EAAG,UAAYA,CACtB,CAEA,SAAgBC,GAAgB,EAA2B,CACzD,OAAOf,KAAYV,EAA4BK,CAAW,CAC5D,CAGA,SAAgBqB,GAAY,EAAkB,EAA8B,CAC1E,IAAMd,EAAMZ,EAA4BK,CAAW,EAC/CmB,IAAY,KAAM,OAAOZ,EAAIF,GAC5B,EAAIA,GAAYc,EACrBtB,EAAWG,EAAaO,CAAG,CAC7B,CAEA,SAAgBe,GAAa,EAAa,EAAuB,CAC1DpB,GAAgB,IAAIjB,CAAE,GAAGiB,GAAgB,IAAIjB,EAAIgC,GAAWhC,CAAE,CAAC,EACpEiC,GAAWjC,EAAIkC,CAAO,CACxB,CAGA,SAAgBI,GAAe,EAAmB,CAChD,IAAMC,EAAWtB,GAAgB,IAAIjB,CAAE,EACnCuC,IAAa,IAAA,IAAWN,GAAWjC,EAAIuC,CAAQ,CACrD,CAIA,SAASC,GAAa,EAAkB,EAAkB,EAAoC,CAC5F,GAAI,CACFC,EAAK,iBAAiBrB,CAAQ,CAAC,CAAC,QAAQsB,CAAK,CAC/C,MAAQ,CAER,CACF,CAGA,SAAgBC,EAAgB,EAAmB,SAAgB,CACjE,OAAO,QAAQjC,EAA0BI,CAAS,CAAC,CAAC,CAAC,SAAS,CAACM,EAAUG,EAAQ,CAAC,KAAO,CACvFiB,GAAaC,EAAMrB,EAAW,GAAO,CACnC,OAAO,QAAQG,CAAK,CAAC,CAAC,SAAS,CAACC,EAAMX,KAAWY,GAAWzB,EAAIwB,EAAMX,CAAK,CAAC,CAC9E,CAAC,CACH,CAAC,EACD,OAAO,QAAQH,EAA4BK,CAAW,CAAC,CAAC,CAAC,SAAS,CAACK,EAAUc,EAAU,MAAQ,CAC7FM,GAAaC,EAAMrB,EAAW,GAAOiB,GAAarC,EAAIkC,CAAO,CAAC,CAChE,CAAC,CACH,CC/FA,SAAgBU,EACd,EACA,EAAY,GACZ,EAC0B,CAC1B,IAAM5C,EAAK,SAAS,cAAcS,CAAG,EAGrC,OAFIoC,IAAW,EAAG,UAAYA,GAC1BC,IAAS,IAAA,KAAW,EAAG,YAAcA,GAClC9C,CACT,CAGA,SAAgB+C,EAAU,EAAmB,EAAgB,CAC3D,EAAK,MAAM,KAAO,GAAGC,EAAI,KAAK,IAC9B,EAAK,MAAM,IAAM,GAAGA,EAAI,IAAI,IAC5B,EAAK,MAAM,MAAQ,GAAGA,EAAI,MAAM,IAChC,EAAK,MAAM,OAAS,GAAGA,EAAI,OAAO,GACpC,CAMA,SAAgBC,EAAQ,EAAa,EAAkB,CAErD,GAAM,CAAE,cAAa,gBAAiBjD,EACtC,MAAO,CACL,EAAGkD,EAAcC,EAAK,MAAQD,EAAc,EAC5C,EAAGE,EAAeD,EAAK,OAASC,EAAe,CACjD,CACF,CAMA,SAAgBC,GAAW,EAAa,EAAgB,CACtD,IAAIC,EAAOH,EAAK,KACZI,EAAMJ,EAAK,IACXK,EAAQL,EAAK,KAAOA,EAAK,MACzBM,EAASN,EAAK,IAAMA,EAAK,OAC7B,IAAK,IAAIO,EAAS1D,EAAG,cAAe0D,EAAQ,EAASA,EAAO,cAAe,CACzE,GAAM,CAAE,YAAW,aAAc,iBAAiBA,CAAM,EACxD,GAAIC,IAAc,WAAaC,IAAc,UAAW,SACxD,IAAMC,EAAOH,EAAO,sBAAsB,EAC1C,EAAO,KAAK,IAAIJ,EAAMO,EAAK,IAAI,EAC/B,EAAM,KAAK,IAAIN,EAAKM,EAAK,GAAG,EAC5B,EAAQ,KAAK,IAAIL,EAAOK,EAAK,KAAK,EAClC,EAAS,KAAK,IAAIJ,EAAQI,EAAK,MAAM,CACvC,CACA,MAAO,CAAE,OAAM,MAAK,MAAO,KAAK,IAAI,EAAGL,EAAQF,CAAI,EAAG,OAAQ,KAAK,IAAI,EAAGG,EAASF,CAAG,CAAE,CAC1F,CAGA,SAAgBO,GAAW,EAAa,EAA8C,CACpF,GAAM,CAAE,cAAa,gBAAiB9D,EAChC+D,EAAW,GAAkB,KAAK,MAAMlD,EAAQ,GAAG,EAAI,IAC7D,MAAO,CACL,MAAOqC,GAAea,EAAQZ,EAAK,KAAK,EACxC,OAAQC,GAAgBW,EAAQZ,EAAK,MAAM,CAC7C,CACF,CC3EA,IAAMa,GAAS,6BAGTC,GAAQ,CACZ,MAAO,8BACP,MAAO,oCACP,MAAO,mCACP,YAAa,2BACb,aAAc,4BACd,UAAW,6BACX,KAAM,wFACN,KACE,4GAEF,QACE,yHAEF,QAAS,2EACX,EAGMC,GAAgB,CACpB,MAAO,sCACP,IAAK,8DACL,KAAM,2DACR,EAKA,SAASC,GAAI,EAAiB,EAAc,EAA6B,CACvE,IAAMnE,EAAK,SAAS,gBAAgBgE,GAAQ,KAAK,EAMjD,OALAhE,EAAG,aAAa,QAAS,MAAM,EAC/BA,EAAG,aAAa,QAAS,OAAOoE,CAAI,CAAC,EACrCpE,EAAG,aAAa,SAAU,OAAOoE,CAAI,CAAC,EACtCpE,EAAG,aAAa,UAAW,OAAOqE,EAAK,GAAGA,GAAM,EAChD,EAAG,UAAYC,EACRtE,CACT,CAEA,IAAauE,GAAQ,EAAgB,EAAO,KAAsBJ,GAAIF,GAAMO,GAAOJ,EAAM,EAAE,EAE9EK,GAAe,GAAkCN,GAAID,GAAcQ,GAAS,GAAI,EAAE,EAM/F,SAAgBC,GAAY,CAACC,EAAIC,EAAIC,EAAIC,GAA8B,CACrE,IAAMC,EAAK,GAAc,EAAI,GAAKC,EAC5BC,EAAK,GAAc,GAAK,GAAKD,EAEnC,OAAOd,GAAI,gDAAgD,SADpCa,EAAEJ,CAAE,EAAE,GAAGM,EAAEL,CAAE,EAAE,GAAGG,EAAEF,CAAE,EAAE,GAAGI,EAAEH,CAAE,EAAE,OACS,KAAM,GAAI,EAAE,CAC/E,C,8z5BC9CMK,GAAW,kBAEbC,GAA2B,KAE/B,SAASC,IAA6B,CAKpC,OAJKD,KACH,GAAO,SAAS,KAAK,YAAY,SAAS,cAAcD,EAAQ,CAAC,EACjEC,GAAK,aAAa,CAAE,KAAM,MAAO,CAAC,CAAC,CAAC,YAAYzC,EAAE,QAAS,GAAI1D,EAAM,CAAC,GAEjEmG,EACT,CAGA,SAAgBE,EAA6B,EAAY,CAEvD,OADAD,GAAc,CAAC,CAAC,WAAY,YAAYE,CAAI,EACrCA,CACT,CAGA,SAAgBC,GAAgB,EAAc,EAAqB,CACjEH,GAAc,CAAC,CAAC,MAAM,YAAYd,EAAM3D,CAAK,CAC/C,CAMA,SAAgB6E,EAAM,EAAmC,CACvD,OAAOF,aAAgB,SAAWA,EAAK,YAAcJ,EACvD,CAGA,SAAgBO,GAAe,EAAyB,CAClD,SAAS,KAAMC,EAAM,EACpB,SAAS,iBAAiB,mBAAoBA,EAAO,CAAE,KAAM,EAAK,CAAC,CAC1E,CC1CA,IAAaC,EAAQ,CAAC,MAAO,QAAS,SAAU,MAAM,EAKtD,SAAgBC,EAAQ,EAAyB,EAAkD,CACjG,IAAMC,EAASC,IAAa,SAAW,SAAW,GAC5CC,EAAQ,GAAe,WAAWC,EAAG,iBAAiB,GAAGF,EAAS,GAAGxB,IAAOuB,GAAQ,CAAC,GAAK,EAChG,MAAO,CAAE,IAAKE,EAAK,KAAK,EAAG,MAAOA,EAAK,OAAO,EAAG,OAAQA,EAAK,QAAQ,EAAG,KAAMA,EAAK,MAAM,CAAE,CAC9F,CCKA,IAAIE,GAA4B,KAE1BC,OAA8B,KAAUb,EAAM3C,EAAE,MAAO,SAAS,CAAC,EAGvE,SAAgByD,EAAO,EAAU,EAAqB,CAChDrD,EAAI,OAAS,GAAKA,EAAI,QAAU,GACpCD,EAAUqD,GAAQ,CAAC,CAAC,YAAYxD,EAAE,MAAO0D,CAAI,CAAC,EAAGtD,CAAG,CACtD,CAGA,SAAgBuD,GAAS,EAAc,EAAc,EAAa,EAAyB,CACzF,IAAMC,EAAQJ,GAAQ,CAAC,CAAC,YAAYxD,EAAE,MAAO,gBAAiBE,CAAI,CAAC,EACnE,EAAM,MAAM,KAAO,GAAGQ,EAAK,IAC3B,EAAM,MAAM,IAAM,GAAGC,EAAI,IACzB,EAAM,MAAM,UAAYkD,CAC1B,CAEA,SAAgBC,IAAmB,CACjCP,IAAO,gBAAgB,CACzB,CCvBA,SAASQ,GAAS,EAAY,EAAkB,EAAoB,CAClE,GAAM,CAAE,MAAK,QAAO,SAAQ,QAASC,EAC/BC,EAAcC,EAAM,OAASvD,EAAME,EACnCsD,EAAYD,EAAM,KAAOA,EAAM,MAAQtD,EACvCwD,EAAaF,EAAM,IAAMA,EAAM,OAASrD,EAK9C,OAJA4C,EAAO,CAAE,KAAMS,EAAM,KAAM,IAAKA,EAAM,IAAK,MAAOA,EAAM,MAAO,OAAQvD,CAAI,EAAG+C,CAAI,EAClFD,EAAO,CAAE,KAAMS,EAAM,KAAM,IAAKE,EAAY,MAAOF,EAAM,MAAO,OAAQrD,CAAO,EAAG6C,CAAI,EACtFD,EAAO,CAAE,KAAMS,EAAM,KAAM,IAAKA,EAAM,IAAMvD,EAAK,MAAOD,EAAM,OAAQuD,CAAY,EAAGP,CAAI,EACzFD,EAAO,CAAE,KAAMU,EAAW,IAAKD,EAAM,IAAMvD,EAAK,MAAOC,EAAO,OAAQqD,CAAY,EAAGP,CAAI,EAClF,CACL,KAAMQ,EAAM,KAAOxD,EACnB,IAAKwD,EAAM,IAAMvD,EACjB,MAAOuD,EAAM,MAAQxD,EAAOE,EAC5B,OAAQqD,CACV,CACF,CAEA,SAAgBI,GAAa,EAAa,EAAW,EAA+B,CAClF,IAAMC,EAAQjE,EAAQjD,EAAImD,CAAI,EACxBgE,EAAY,IAAyB,CACzC,IAAKC,EAAM,IAAMF,EAAM,EACvB,MAAOE,EAAM,MAAQF,EAAM,EAC3B,OAAQE,EAAM,OAASF,EAAM,EAC7B,KAAME,EAAM,KAAOF,EAAM,CAC3B,GACMG,EAASF,EAASrB,EAAQI,EAAI,QAAQ,CAAC,EAO7CS,GAAS,CALP,KAAMxD,EAAK,KAAOkE,EAAO,KACzB,IAAKlE,EAAK,IAAMkE,EAAO,IACvB,MAAOlE,EAAK,MAAQkE,EAAO,KAAOA,EAAO,MACzC,OAAQlE,EAAK,OAASkE,EAAO,IAAMA,EAAO,MAEnC,EAAWA,EAAQ,QAAQ,EAEpChB,EAAOM,GADYA,GAASxD,EAAMgE,EAASrB,EAAQI,EAAI,QAAQ,CAAC,EAAG,QACnD,EAAYiB,EAASrB,EAAQI,EAAI,SAAS,CAAC,EAAG,SAAS,EAAG,aAAa,CACzF,CAGA,SAAgBoB,GAAoB,EAAa,EAAW,EAA+B,CACzF,IAAMC,EAAQ,MAAM,KAAKvH,EAAG,QAAQ,CAAC,CAClC,OAAQ,GAAU,CACjB,GAAI0F,EAAM8B,CAAK,EAAG,MAAO,GACzB,IAAMC,EAAa,iBAAiBD,CAAK,EACzC,OAAOC,EAAW,UAAY,QAAUA,EAAW,aAAe,QACpE,CAAC,CAAC,CACD,IAAK,GAAUD,EAAM,sBAAsB,CAAC,EAG/C,GAFAD,EAAM,QAAS,GAAclB,EAAOqB,EAAW,OAAO,CAAC,EAEnD,CAACxB,EAAG,QAAQ,SAAS,MAAM,EAAG,OAClC,IAAMyB,EAAQzB,EAAG,cAAc,WAAW,KAAK,EAC/CqB,EAAM,MAAM,EAAG,IAAOI,EAAQC,EAAE,KAAOC,EAAE,KAAOD,EAAE,IAAMC,EAAE,GAAI,EAC9D,IAAK,IAAIC,EAAI,EAAGA,EAAIP,EAAM,OAAS,EAAG,IAAK,CACzC,IAAMK,EAAIL,EAAMO,GACVD,EAAIN,EAAMO,EAAI,GACpBzB,EACEsB,EACI,CAAE,KAAMC,EAAE,MAAO,IAAKzE,EAAK,IAAK,MAAO0E,EAAE,KAAOD,EAAE,MAAO,OAAQzE,EAAK,MAAO,EAC7E,CAAE,KAAMA,EAAK,KAAM,IAAKyE,EAAE,OAAQ,MAAOzE,EAAK,MAAO,OAAQ0E,EAAE,IAAMD,EAAE,MAAO,EAClF,KACF,CACF,CACF,CCrEA,IAAMG,GAAiB,CAAE,EAAG,GAAI,EAAG,EAAG,EAMhCC,GAAS,CACb,EAAG,CAAC,yBAA0B,sBAAsB,EACpD,EAAG,CAAC,uBAAwB,oCAAoC,CAClE,EAEMC,IAAY,EAAgB,IAChCnB,EAAM,MAAQoB,EAAM,MACpBpB,EAAM,OAASoB,EAAM,OACrBpB,EAAM,KAAOoB,EAAM,KACnBpB,EAAM,QAAUoB,EAAM,OAGxB,SAASC,GAAS,EAAa,EAAa,EAAa,EAAqB,CAC5E,IAAMC,EAAK,KAAK,IAAIC,EAAKC,CAAG,EACtBC,EAAK,KAAK,IAAIC,EAAKC,CAAG,EAC5B,OAAOL,EAAKG,GAAMH,EAAKG,GAAM,GAAKF,EAAMG,GAAO,CACjD,CAIA,IAAME,GAAgB,GAA2B,OAAO,KAAK,MAAMC,EAAS,EAAE,EAAI,EAAE,EAMpF,SAAgBC,GAAc,EAAY,EAAY,EAAoB,CACxE,IAAM1D,EAAIiD,GAASP,EAAE,IAAKA,EAAE,OAAQC,EAAE,IAAKA,EAAE,MAAM,EAC7C7C,EAAImD,GAASP,EAAE,KAAMA,EAAE,MAAOC,EAAE,KAAMA,EAAE,KAAK,EAC7CgB,EAAiB,CACrB,CAAC,KAAK,IAAIjB,EAAE,KAAMC,EAAE,IAAI,EAAG,KAAK,IAAID,EAAE,KAAMC,EAAE,IAAI,EAAG,GAAG,EACxD,CAAC,KAAK,IAAID,EAAE,MAAOC,EAAE,KAAK,EAAG,KAAK,IAAID,EAAE,MAAOC,EAAE,KAAK,EAAG,GAAG,EAC5D,CAAC,KAAK,IAAID,EAAE,IAAKC,EAAE,GAAG,EAAG,KAAK,IAAID,EAAE,IAAKC,EAAE,GAAG,EAAG,GAAG,EACpD,CAAC,KAAK,IAAID,EAAE,OAAQC,EAAE,MAAM,EAAG,KAAK,IAAID,EAAE,OAAQC,EAAE,MAAM,EAAG,GAAG,CAClE,EACMiB,EAAe,CACnB,CAAClB,EAAE,MAAOC,EAAE,KAAM,GAAG,EACrB,CAACA,EAAE,MAAOD,EAAE,KAAM,GAAG,EACrB,CAACA,EAAE,OAAQC,EAAE,IAAK,GAAG,EACrB,CAACA,EAAE,OAAQD,EAAE,IAAK,GAAG,CACvB,EAEMmB,EAAc,CAAE,EAAG,EAAG,EAAG,CAAE,GAChCd,GAASL,EAAGC,CAAC,GAAKI,GAASJ,EAAGD,CAAC,EAAIiB,EAASC,EAAA,CAAM,SAAS,CAACE,EAAMC,EAAIC,KAAU,CAC/E,IAAMC,EAASF,EAAKD,EACpB,GAAIG,EAAS,GAAK,OAClB,IAAMC,EAASJ,EAAOG,EAAS,EAEzB1C,EADO0C,GAAUpB,GAAemB,GACb,wBAA0BlB,GAAOkB,EAAK,CAAC,EAAYA,EAAK,GAAK,GAClFA,IAAS,KACX7C,EAAO,CAAE,KAAM2C,EAAM,IAAK9D,EAAG,MAAOiE,EAAQ,OAAQ,CAAE,EAAG,cAAc,EACvE5C,GAASmC,GAAaS,EAASjC,EAAM,CAAC,EAAGkC,EAAQlE,EAAGuB,CAAS,IAE7DJ,EAAO,CAAE,KAAMrB,EAAG,IAAKgE,EAAM,MAAO,EAAG,OAAQG,CAAO,EAAG,cAAc,EACvE5C,GAASmC,GAAaS,EAASjC,EAAM,CAAC,EAAGlC,EAAGoE,EAAQ3C,CAAS,EAEjE,CAAC,CACH,CC7DA,IAAM4C,GAAiB,uCAGvB,SAAgBC,GAAQ,EAA6B,CACnD,OAAOtJ,EAAG,eAAe,QAAQqJ,EAAc,GAAK,IACtD,CAaA,SAAgBE,EAAc,EAAa,EAAiC,CAC1E,IAAMC,EAAQF,GAAQtJ,CAAE,EACxB,GAAI,CAACwJ,EAAO,OAAO,KACnB,IAAMC,EAAYD,EAAM,sBAAsB,EACxCtC,EAAQjE,EAAQuG,EAAOC,CAAS,EAChCC,EAAS,GAAkB,KAAK,MAAM7I,EAAQ,EAAE,EAAI,GACpDmE,EAAI0E,GAAOvG,EAAK,KAAOsG,EAAU,MAAQvC,EAAM,CAAC,EAChDhC,EAAIwE,GAAOvG,EAAK,IAAMsG,EAAU,KAAOvC,EAAM,CAAC,EACpD,MAAO,CACL,IACA,IACA,KAAM,KAAKlC,EAAE,OAAOE,IACpB,MAAOsE,EAAM,QAAQ,cAAc,CAAC,EAAE,aAAa,YAAY,GAAK,EACtE,CACF,CChCA,IAAMG,GAAY,EAEZC,GAAY,IAAI,IAAI,CAAC,QAAS,QAAS,IAAI,CAAC,EAErCC,GAAkC,GAAO,CACpD,IAAIpH,EAAuBzC,EAC3B,IAAK,IAAI8J,EAAQ,EAAGrH,GAAQqH,GAASH,GAAW,EAAOlH,EAAK,cAAe,IAAS,CAClF,GAAI,CAACA,EAAK,UAAU,SAAS,GAAG,EAAG,SACnC,IAAMlB,EAAQ,OAAO,YACnB,MAAM,KAAKkB,EAAK,UAAU,CAAC,CACxB,OAAQ,GAAc,CAACmH,GAAU,IAAIG,EAAU,IAAI,CAAC,CAAC,CAErD,IAAK,GAAc,CAACA,EAAU,KAAMA,EAAU,QAAU,IAAYA,EAAU,KAAK,CAAC,CACzF,EACA,MAAO,CAAE,KAAMtH,EAAK,UAAW,OAAQA,IAASzC,EAAI,QAASyC,EAAM,OAAM,CAC3E,CACA,OAAO,IACT,EChBA,SAASuH,GAAc,EAAwC,CAC7D,GAAI,CACF,IAAMzI,EAAQ,KAAK,MAAMkB,EAAK,aAAa,YAAY,GAAK,IAAI,EAChE,OAAOlB,GAAS,OAAOA,GAAU,SAAWA,EAAQ,CAAC,CACvD,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAEA,IAAa0I,GAAsC,GAAO,CACxD,IAAMxH,EAAOzC,EAAG,QAAQ,kBAAkB,EACpCwE,EAAO/B,GAAM,aAAa,gBAAgB,EAEhD,MADI,CAACA,GAAQ,CAAC+B,EAAa,KACpB,CACL,OACA,WAAY/B,EAAK,aAAa,aAAa,GAAK,IAAA,GAChD,OAAQA,IAASzC,EACjB,QAASyC,EACT,MAAOuH,GAAcvH,CAAI,CAC3B,CACF,ECMIyH,EAAiD,KAGrD,SAASC,IAAoD,CAC3D,GAAID,EAAc,OAAOA,EACzB,EAAe,IAAI,IACnB,IAAK,IAAME,KAAU,SAAS,iBAAiB,kBAAkB,EAAG,CAClE,IAAMC,EAAS,0CAA0C,MACtDD,EAAO,aAAe,GAAA,CAAI,MAAM,EAAG,GAAO,CAC7C,EACA,GAAKC,EACL,GAAI,CACF,GAAM,CAAE,YAAW,cAAe,KAAK,MAAMA,EAAO,EAAE,EAIhDC,EAAY,OAA8DC,GAChFC,EAAW,QAAS,GAAU,CACxBF,IAAWG,EAAM,OAAOP,EAAc,IAAII,EAASG,EAAM,MAAOA,CAAK,CAC3E,CAAC,CACH,MAAQ,CAER,CACF,CACA,OAAOP,CACT,CAEA,SAASQ,GAAQ,EAA2B,CAC1C,IAAM/J,EAAM,OAAO,KAAKX,CAAE,CAAC,CAAC,KAAM,GAASwE,EAAK,WAAW,eAAe,CAAC,EAC3E,OAAO7D,EAAOX,EAAwCW,GAAO,IAC/D,CAGA,SAASgK,GAAY,EAAmC,CACtD,IAAInF,EAAqBoF,EACzB,KAAOpF,GAAQ,OAAOA,EAAK,MAAS,UAAU,EAAOA,EAAK,MAC1D,OAAOA,GAAM,qBAAqB,QAAUA,EAAK,UAAY,IAAA,EAC/D,CCxDA,IAAMqF,GAA6B,CAACZ,GD8DY,GAAO,CACrD,IAAMa,EAAQX,GAAuB,EACjCY,EAA+B,KAC/BC,EAAS,GACTJ,EAAQF,GAAQ1K,CAAE,CAAC,EAAE,QAAU,KACnC,IAAK,IAAI8J,EAAQ,EAAGc,GAASd,EAAQ,GAAI,EAAQc,EAAM,OAAQ,IAAS,CACtE,IAAMK,EAAOL,EAAM,KAEnB,GADI,OAAOK,GAAS,WAAU,EAAS,IACnC,CAACA,GAAQ,OAAOA,GAAS,SAAU,SACvC,IAAM1J,EAAQqJ,EAAM,eAAiB,CAAC,EAChCH,EAAQK,EAAM,IAAIG,CAAI,EAC5B,GAAIR,EAAO,MAAO,CAAE,GAAGA,EAAO,SAAQ,QAAO,QAASE,GAAYC,CAAK,CAAE,EAEzE,GAAM,CAAE,cAAa,OAAM,qBAAsBK,EAE3CC,GAAWC,GAAe3G,GAAQ,GAAA,CAAI,QAAQ,OAAQ,EAAE,EACxD4G,EAAS,SAAS,KAAKF,CAAO,GAAK,CAAC,wBAAwB,KAAKA,CAAO,EAC1E,CAACH,GAAU,CAACC,GAAU,CAACK,GAAqBD,IAC9C,EAAS,CAAE,KAAMF,EAAS,SAAQ,QAAO,QAASP,GAAYC,CAAK,CAAE,EAEzE,CACA,OAAOG,CACT,ECpFuElB,EAAa,EAGpF,SAAgByB,GAAY,EAAmC,CAC7D,IAAK,IAAMC,KAAQV,GACjB,GAAI,CACF,IAAMW,EAAYD,EAAKvL,CAAE,EACzB,GAAIwL,EAAW,OAAOA,CACxB,MAAQ,CAER,CAEF,OAAO,IACT,CAeA,SAAgBC,GAAe,CAAE,SAAyC,CACxE,OAAO,OAAO,QAAQlK,CAAK,CAAC,CACzB,QAAQ,CAACiD,KAAU,CAAC,CAAC,WAAY,YAAa,OAAO,CAAC,CAAC,SAASA,CAAI,CAAC,CAAC,CACtE,QAAQ,EAAG3D,KAAW,CAAC,SAAU,SAAU,SAAS,CAAC,CAAC,SAAS,OAAOA,CAAK,CAAC,CAAC,CAC7E,KAAK,CAAC2D,EAAM3D,MAAY,CAAE,OAAM,MAAO,OAAOA,CAAK,CAAE,EAAE,CAC5D,CCxCA,SAAgB6K,EAAY,EAAqB,CAC/C,IAAMF,EAAYF,GAAYtL,CAAE,EAChC,OAAOwL,GAAa,CAACA,EAAU,OAASA,EAAU,KAAOxL,EAAG,SAC9D,CAGA,SAAgB2L,EAAS,EAAa,EAAY3L,EAAG,sBAAsB,EAAW,CACpF,GAAM,CAAE,QAAO,UAAW8D,GAAW9D,EAAImD,CAAI,EAC7C,MAAO,GAAGyI,EAAM,KAAKC,GACvB,CAMA,SAAgBC,GAAc,EAAa,EAAgC,CACzE,IAAMtH,EAAO,SAAS,cAAc,MAAM,EAC1C,EAAK,UAAY3B,EACjB,EAAK,YAAc7C,EAAG,UACtB,IAAM+L,EAAQ/L,EAAG,UAAU,GAC3B,GAAI+L,EAAO,CACT,IAAMC,EAAUxH,EAAK,YAAY,SAAS,cAAc,MAAM,CAAC,EAC/D,EAAQ,UAAY,UACpB,EAAQ,YAAc,IAAMuH,CAC9B,CACA,OAAOvH,CACT,CC3BA,IAAMyH,GAAM,EACNC,GAAO,EAETC,EAA8B,KAGlC,SAAgBC,GAAY,EAAa,EAAiB,CACxD,IAAY7G,EAAM3C,EAAE,MAAO,SAAS,CAAC,EACrC,IAAMyJ,EAAW9C,EAAcvJ,EAAImD,CAAI,CAAC,EAAE,MAAQ,GAClDgJ,EAAQ,gBACNvJ,EAAE,IAAK,GAAI8I,EAAY1L,CAAE,CAAC,EAC1B4C,EAAE,OAAQ,GAAI+I,EAAS3L,EAAImD,CAAI,CAAC,EAChCP,EAAE,OAAQ,WAAYyJ,CAAQ,CAChC,EACA,EAAQ,MAAM,QAAU,OAExB,IAAMC,EAAQnJ,EAAK,IAAMgJ,EAAQ,aAAeF,GAC1C1I,EAAM+I,GAASJ,GAAOI,EAAQnJ,EAAK,IAAMA,EAAK,OAAS8I,GACvDM,EAAU,OAAO,WAAaJ,EAAQ,YAAcD,GAC1D,EAAQ,MAAM,IAAM,GAAG3I,EAAI,IAC3B,EAAQ,MAAM,KAAO,GAAG,KAAK,IAAI2I,GAAM,KAAK,IAAI/I,EAAK,KAAMoJ,CAAO,CAAC,EAAE,GACvE,CAEA,SAAgBC,IAAoB,CAC9BL,IAAS,EAAQ,MAAM,QAAU,OACvC,CCjBA,IAAIM,GAAU,GACVC,GAAS,GACTC,GAA0B,KAEjBC,MAA8BH,IAAWC,GAEtD,SAASG,IAAmB,CAC1BnG,GAAW,EACX8F,GAAY,EACZ,GAAU,IACZ,CAGA,SAASM,IAAe,CACtB,SAAS,KAAK,MAAM,OAASF,EAAa,EAAI,YAAc,GACvDA,EAAa,GAAGC,GAAW,CAClC,CAGA,SAAgBE,GAAe,EAAmB,CAChD,GAASC,EACTF,GAAO,CACT,CAEA,SAASG,GAAQ,EAAkC,CACjD,GAAIC,IAAWP,IAAWjH,EAAMwH,CAAM,EAAG,OACzC,GACE,EAAEA,aAAkB,UACpBA,IAAW,SAAS,MACpBA,IAAW,SAAS,gBACpB,CACAL,GAAW,EACX,MACF,CACAnG,GAAW,EACX,GAAUwG,EAEV,IAAM/J,EAAO+J,EAAO,sBAAsB,EACpC1N,EAASE,EAAc,EAC7B,GAAIF,GAAUA,IAAW0N,GAAU1N,EAAO,YAAa,CAGrD,IAAM2N,EAAa3N,EAAO,sBAAsB,EAChD6G,EAAOlD,EAAM,QAAQ,EACrByF,GAAcuE,EAAYhK,EAAMF,EAAQzD,EAAQ2N,CAAU,CAAC,CAC7D,KAAO,CACL,IAAMjH,EAAK,iBAAiBgH,CAAM,EAClCjG,GAAaiG,EAAQ/J,EAAM+C,CAAE,EAC7BoB,GAAoB4F,EAAQ/J,EAAM+C,CAAE,CACtC,CACAkG,GAAYc,EAAQ/J,CAAI,CAC1B,CAEA,SAAgBiK,IAAmB,CACjC,IAAMC,EAAU,GAAkB,CAChC,GAAUC,EACVR,GAAO,CACT,EACA,SAAS,iBAAiB,UAAY,GAAM,CACtCS,EAAE,MAAQ,OAAOF,EAAO,EAAI,CAClC,CAAC,EACD,SAAS,iBAAiB,QAAU,GAAM,CACpCE,EAAE,MAAQ,OAAOF,EAAO,EAAK,CACnC,CAAC,EAED,OAAO,iBAAiB,WAAcA,EAAO,EAAK,CAAC,EAEnD,SAAS,iBACP,YACC,GAAM,CACDT,EAAa,GAAGK,GAAQ,EAAE,MAAM,CACtC,EACA,EACF,EACA,SAAS,iBAAiB,iBAAoB,CACxCL,EAAa,GAAGC,GAAW,CACjC,CAAC,EAGD,IAAK,IAAM5B,IAAQ,CAAC,cAAe,YAAa,YAAa,UAAW,OAAO,EAC7E,SAAS,iBACPA,EACC,GAAM,CACA2B,EAAa,GAAK,GAAMW,EAAE,MAAM,IACrCA,EAAE,eAAe,EACjBA,EAAE,gBAAgB,EACdtC,IAAS,SAAWsC,EAAE,kBAAkB,SAASxN,EAAIwN,EAAE,MAAM,EACnE,EACA,EACF,CAEJ,CCxGA,IAAMC,GAAU,kBAAkB,KAAK,UAAU,QAAQ,EAG5CC,GAAUD,GAAU,IAAM,MAG1BE,GAAcF,GAAU,IAAM,OCG3C,SAAgBG,IAAsB,CACpChI,OAAqB,CACnB,IAAMiI,EAAOrI,EAAM3C,EAAE,MAAO,UAAU,CAAC,EACvCgL,EAAK,aAAa,OAAQ,QAAQ,EAClC,EAAK,MAAQ,uBACb,IAAMC,EAAOjL,EAAE,OAAQ,OAAQ,MAAM,EACrCiL,EAAK,YAAYjL,EAAE,MAAO,GAAI6K,EAAO,CAAC,EACtC,IAAMK,EAAMlL,EAAE,SAAU,KAAK,EAC7BkL,EAAI,aAAa,aAAc,kBAAkB,EACjDA,EAAI,YAAYvJ,EAAK,QAAS,EAAE,CAAC,EACjCqJ,EAAK,OACHrJ,EAAK,SAAS,EACd3B,EAAE,OAAQ,GAAI,SAAS,EACvBA,EAAE,OAAQ,MAAO,GAAG,EACpBiL,EACAjL,EAAE,OAAQ,SAAU,WAAW,EAC/BkL,CACF,EAEA,IAAMC,EAAa,GAAoB,CACrCH,EAAK,UAAU,OAAO,YAAalB,CAAM,EACzCK,GAAeL,CAAM,CACvB,EACAkB,EAAK,iBAAiB,YAAeG,EAAU,EAAI,CAAC,EACpDD,EAAI,iBAAiB,QAAU,GAAM,CACnC,EAAE,gBAAgB,EAClBC,EAAU,EAAK,CACjB,CAAC,EACD,SAAS,iBAAiB,UAAY,GAAM,CACtC,EAAE,MAAQ,UAAUA,EAAU,EAAK,CACzC,CAAC,CACH,CAAC,CACH,CCpCA,SAAgBC,GAAY,EAAqB,CAC/C,IAAMC,EAAkB,CAAC,EACzB,IACE,IAAIzI,EAAOxF,EACXwF,EAAK,eAAiBA,IAAS,SAAS,KACxC,EAAOA,EAAK,cACZ,CACA,IAAM0I,EAAK1I,EAAK,aAAa,IAAI,EACjC,GAAI0I,EAAI,MAAO,CAAC,IAAM,IAAI,OAAOA,CAAE,EAAG,GAAGD,CAAK,CAAC,CAAC,KAAK,KAAK,EAC1D,IAAME,EAAU,MAAM,KAAK3I,EAAK,cAAc,QAAQ,CAAC,CAAC,OACrD,GAAY4I,EAAQ,YAAc5I,EAAK,SAC1C,EACMwG,EAAU,MAAM,KAAKxG,EAAK,UAAY,GAAS,IAAM,IAAI,OAAOhB,CAAI,CAAC,CAAC,CAAC,KAAK,EAAE,EAC9E6J,EAAMF,EAAQ,OAAS,EAAI,gBAAgBA,EAAQ,QAAQ3I,CAAI,EAAI,EAAE,GAAK,GAChFyI,EAAM,QAAQ,IAAI,OAAOzI,EAAK,SAAS,EAAIwG,EAAUqC,CAAG,CAC1D,CACA,MAAO,CAAC,OAAQ,GAAGJ,CAAK,CAAC,CAAC,KAAK,KAAK,CACtC,CCnBA,IAAMK,GAAQ,UAGd,SAAgBC,GAAY,EAAqB,CAC/C,IAAMC,EAAM,IAAI,IAAI,SAAS,IAAI,EACjCA,EAAI,aAAa,IAAIF,GAAON,GAAYhO,CAAE,CAAC,EAC3C,IAAMwJ,EAAQxJ,EAAG,QAAQ,aAAa,EAEtC,OADIwJ,IAAO,EAAI,KAAOA,EAAM,IACrBgF,EAAI,IACb,CAMA,SAAgBC,IAAoB,CAClC,IAAMrN,EAAW,IAAI,gBAAgB,SAAS,MAAM,CAAC,CAAC,IAAIkN,EAAK,EAC/D,GAAI,CAAClN,EAAU,OACf,IAAIsN,EAAQ,EACNC,EAAQ,gBAAkB,CAC9B,IAAI3O,EAAqB,KACzB,GAAI,CACF,EAAK,SAAS,cAAcoB,CAAQ,CACtC,MAAQ,CAER,CACIpB,GAAM,CAAC0F,EAAM1F,CAAE,IACjBA,EAAG,eAAe,CAAE,MAAO,QAAS,CAAC,EACrCD,EAAIC,CAAE,IAEJA,GAAM,EAAE0O,EAAQ,KAAI,cAAcC,CAAK,CAC7C,EAAG,GAAG,CACR,CCjCA,IAAaC,EAAU,CAErB,KAAM,KAEN,SAAU,EAEV,gBAAiB,GAEjB,aAAc,GAEd,OAAQ,GAER,YAAa,KAEb,YAAa,EACf,ECNMC,GAAa,GAEfC,EAAoC,KACpCC,GAA6B,KAC7BC,EAAmC,KACnC3K,EAA2B,KAC3B4K,EAA2B,KAC3BC,GAAQ,EAOZ,SAASC,GAAU,EAAuB,CACxC,IAAS5J,EAAM3C,EAAE,MAAO,MAAM,CAAC,EAC/B,IAAM4G,EAAQF,GAAQ9J,CAAM,GAAK,SAAS,KACpC2D,EAAOqG,EAAM,sBAAsB,EACnCtC,EAAQjE,EAAQuG,EAAOrG,CAAI,EAC3BiM,EAAU/L,GAAWmG,EAAOrG,CAAI,EACtCJ,EAAUsB,EAAM+K,CAAO,EACvB,IAAMC,EAAS,GAAGlM,EAAK,KAAOiM,EAAQ,KAAK,KAAKjM,EAAK,IAAMiM,EAAQ,IAAI,IACvE/K,EAAK,MAAM,YAAY,wBAAyBgL,CAAM,EACtDhL,EAAK,MAAM,YAAY,gBAAiBgL,CAAM,EAC9ChL,EAAK,UAAU,OAAO,OAAQuK,EAAQ,WAAa,CAAC,EACpD,IAAMU,EAAO,GAAGV,EAAQ,SAAW1H,EAAM,EAAE,KAAK0H,EAAQ,SAAW1H,EAAM,EAAE,IAC3E7C,EAAK,MAAM,YAAY,oBAAqBiL,CAAI,EAChDjL,EAAK,MAAM,YAAY,YAAaiL,CAAI,CAC1C,CAGA,SAASC,IAAa,CACpB,IAAM/P,EAASE,EAAc,EACvB8P,EAAWZ,EAAQ,SAAW,GAAKpP,IAAW,KACpD,GAAIyP,GAAYH,EAAe,CAC7B,IAAM3L,EAAO8L,EAAS,sBAAsB,EAC5ClM,EAAU+L,EAAe3L,CAAI,EAC7B4L,IAAQ,UAAU,OAAO,YAAa5L,EAAK,IAAM0L,EAAU,EAE3D,EAAc,MAAM,WAAaI,EAAS,YAAc,GAAK,QAC/D,CACIO,GAAUL,GAAU3P,CAAM,EAC1B6E,IAAM,EAAK,MAAM,QAAUmL,EAAW,QAAU,QACpD,GAAQP,GAAYO,EAAW,sBAAsBD,EAAI,EAAI,CAC/D,CAEA,SAASE,IAAqB,CAC5B,AAAYP,KAAQ,sBAAsBK,EAAI,CAChD,CAGA,SAAgBG,GAAU,EAAmB,CACtCd,EAAQ,kBACRE,IACH,EAAgBvJ,EAAM3C,EAAE,MAAO,wBAAwB,CAAC,EACxD,GAASkM,EAAc,YAAYlM,EAAE,MAAO,SAAS,CAAC,GAExDmM,IAAQ,gBAAgBnM,EAAE,IAAK,GAAI8I,EAAY1L,CAAE,CAAC,EAAG2L,EAAS3L,CAAE,CAAC,EACjE,EAAc,MAAM,QAAU,QAC9B,EAAWA,EACXyP,GAAa,EACf,CAGA,SAAgBE,IAAqB,CACnC,IAAMnQ,EAASE,EAAc,EACzBF,GAAUoP,EAAQ,gBACpBc,GAAUlQ,CAAM,GAEZsP,IAAe,EAAc,MAAM,QAAU,QACjD,EAAW,MAEbW,GAAa,CACf,CAGA,SAAgBG,GAAiB,EAAmB,CAClD,IAAiBrK,EAAM3C,EAAE,MAAO,uBAAuB,CAAC,EACxDG,EAAUiM,EAAchP,EAAG,sBAAsB,CAAC,EAClD,EAAa,MAAM,QAAU,OAC/B,CAEA,SAAgB6P,IAAyB,CACnCb,IAAc,EAAa,MAAM,QAAU,OACjD,CAEA,SAAgBc,IAAsB,CACpCnQ,MAAkB,CAChBgQ,GAAa,EACbE,GAAiB,CACnB,CAAC,CACH,CC5FA,IAAME,EAAS,EAEXC,EAA2B,KAE/B,SAASC,IAAkB,CACpBD,IACLA,EAAK,OAAO,EACZ,EAAO,KACPH,GAAiB,EACnB,CAEA,SAASK,GAAS,EAAkB,EAAwB,CAC1DD,GAAU,EACV,IAAME,EAAS,SACZ,kBAAkBC,EAAUC,CAAQ,CAAC,CACrC,OAAQ,GAAO,CAAC3K,EAAM1F,CAAE,GAAKA,IAAO,SAAS,MAAQA,IAAO,SAAS,eAAe,EAEjFsQ,EAAQ,EAAO/K,EAAM3C,EAAE,MAAO,MAAM,CAAC,EAC3C0N,EAAK,aAAa,OAAQ,MAAM,EAEhCA,EADkB,YAAY1N,EAAE,MAAO,WAAW,CAClD,CAAA,CAAK,OAAOA,EAAE,OAAQ,GAAI,mBAAmB,EAAGA,EAAE,OAAQ,QAAS,OAAOuN,EAAO,MAAM,CAAC,CAAC,EACzF,IAAK,IAAMhK,KAASgK,EAAQ,CAC1B,IAAMI,EAAOD,EAAK,YAAY1N,EAAE,MAAO,WAAW,CAAC,EACnD2N,EAAK,aAAa,OAAQ,UAAU,EACpCA,EAAK,OAAOzE,GAAc3F,EAAO,YAAY,EAAGvD,EAAE,OAAQ,OAAQ+I,EAASxF,CAAK,CAAC,CAAC,EAClFoK,EAAK,iBAAiB,iBAAoBX,GAAiBzJ,CAAK,CAAC,EACjEoK,EAAK,iBAAiB,YAAe,CACnCN,GAAU,EACVlQ,EAAIoG,CAAK,CACX,CAAC,CACH,CAEA,IAAM7C,EAAO,KAAK,IAAI8M,EAAWL,EAAQ,OAAO,WAAaO,EAAK,YAAcP,CAAM,EAChFxM,EAAM,KAAK,IAAI8M,EAAWN,EAAQ,OAAO,YAAcO,EAAK,aAAeP,CAAM,EACvF,EAAK,MAAM,KAAO,GAAGzM,EAAK,IAC1B,EAAK,MAAM,IAAM,GAAGC,EAAI,GAC1B,CAEA,SAAgBiN,IAAuB,CACrC,SAAS,iBACP,cACC,GAAM,CACA5D,EAAa,GAAK,GAAM,EAAE,MAAM,IACrC,EAAE,eAAe,EACjB,EAAE,gBAAgB,EAClBsD,GAAS,EAAE,QAAS,EAAE,OAAO,EAC/B,EACA,EACF,EAEA,SAAS,iBACP,YACC,GAAM,CACDF,GAAQ,CAAC,EAAE,aAAa,CAAC,CAAC,SAASA,CAAI,GAAGC,GAAU,CAC1D,EACA,EACF,CACF,CCnCA,IAAMQ,GAAgB,qEAEtB,SAASC,GAAW,EAAoD,CACtE,GAAI,CACF,IAAMC,EAAO,KAAK,MAAMC,GAAQ,EAAE,EAElC,OADcD,GAAQ,OAAOA,EAAK,SAAY,UAAY,MAAM,QAAQA,EAAK,MAAM,EACpEA,EAAO,IACxB,MAAQ,CACN,OAAO,IACT,CACF,CAGA,IAAaE,GAAe,GAC1BH,GAAWD,GAAc,KAAKK,CAAI,CAAC,GAAG,EAAE,EAG7BC,OACXL,GAAW,SAAS,eAAe,SAAS,CAAC,EAAE,WAAW,EAE5D,SAASM,GAAW,EAAmC,CACrD,IAAMC,EAAS,IAAI,IACnB,IAAK,IAAMC,KAAQpO,EAAK,MAAM;CAAI,EAAG,CACnC,IAAMqO,EAAUD,EAAK,KAAK,EACtBC,GAASF,EAAO,IAAIE,GAAUF,EAAO,IAAIE,CAAO,GAAK,GAAK,CAAC,CACjE,CACA,OAAOF,CACT,CAOA,SAASG,GAAW,EAA6B,EAA4C,CAC3F,MAAO,CAAC,GAAGH,CAAM,CAAC,CAAC,QAAQ,CAACC,EAAM,KAAO,GAAKG,EAAY,IAAIH,CAAI,GAAK,EAAE,CAAC,CAAC,KAAK,CAACA,KAAUA,CAAI,CACjG,CAEA,SAAgBI,GAAY,EAAmB,EAAiC,CAC9E,IAAMhI,GAAW,EAAkB,IAAiBqH,EAAK,OAAO,KAAM,GAAM9I,EAAE,OAAS0J,CAAI,EACrFC,GAAW,EAAkB,IAAiBlI,EAAQqH,EAAMY,CAAI,CAAC,EAAE,OAASA,EAC5EE,EAAQ,OAAO,KAAKC,EAAQ,OAAO,EAEzC,MAAO,CACL,QAASD,EACN,OAAQ,GAASF,KAAQI,EAAM,SAAWA,EAAM,QAAQJ,KAAUG,EAAQ,QAAQH,EAAK,CAAC,CACxF,IAAK,GAAS,CACb,IAAMK,EAASZ,GAAWW,EAAM,QAAQJ,EAAK,EACvCM,EAAQb,GAAWU,EAAQ,QAAQH,EAAK,EAC9C,MAAO,CACL,MAAOC,EAAQE,EAASH,CAAI,EAC5B,GAAIjI,EAAQoI,EAASH,CAAI,CAAC,EAAE,GAC5B,MAAOH,GAAWS,EAAOD,CAAM,EAC/B,QAASR,GAAWQ,EAAQC,CAAK,CACnC,CACF,CAAC,EACH,MAAOJ,EAAM,OAAQ,GAAS,EAAEF,KAAQI,EAAM,QAAQ,CAAC,CAAC,IAAK,GAASH,EAAQE,EAASH,CAAI,CAAC,EAC5F,QAAS,OAAO,KAAKI,EAAM,OAAO,CAAC,CAChC,OAAQ,GAAS,EAAEJ,KAAQG,EAAQ,QAAQ,CAAC,CAC5C,IAAK,GAASF,EAAQG,EAAOJ,CAAI,CAAC,CACvC,CACF,CCpFA,SAAgBO,EAA0C,EAAoB,EAAqB,CACjG,IAAMtM,EAAOuM,EAAM,cAAiB3Q,CAAQ,EAC5C,GAAI,CAACoE,EAAM,MAAU,MAAM,6BAA6BpE,EAAS,EAAE,EACnE,OAAOoE,CACT,CAGA,IAAawM,GAAW,EAAoB,IAC1CF,EAAKC,EAAO,mBAAmBE,EAAI,GAAG,ECDlCC,GAAY,GACZC,GAAkB,IAEpBC,EACAjT,EACAmT,EAEEC,OAAyBD,EAAU,MAAM,EAG/C,SAASE,GAAU,EAA4B,CAC7C,IAAMhO,EAAO5B,EAAE,OAAQ,YAAY,EAC7B,CAACrC,EAAQ,GAAGkS,GAAQC,EAAM,MAAM,KAAK,EAG3C,OAFID,EAAK,OAAQjO,EAAK,OAAO5B,EAAE,OAAQ,SAAUrC,EAAS,KAAK,EAAGkS,EAAK,KAAK,KAAK,CAAC,EAC7E,EAAK,YAAcC,EACjBlO,CACT,CAGA,SAASmO,GAAW,EAAkB,EAA+B,CACnE,IAAMC,EAAShQ,EAAE,SAAUiQ,EAAU,uBAAyB,aAAa,EAG3E,OAFAD,EAAO,OAAOrO,EAAK,MAAM,EAAG3B,EAAE,OAAQ,YAAakQ,CAAQ,EAAGlQ,EAAE,OAAQ,SAAU,QAAQ,CAAC,EAC3FgQ,EAAO,iBAAiB,QAASL,EAAU,EACpCK,CACT,CAEA,SAASG,GAAa,EAAe,EAAqB,CAExDV,EADqB,YAAYzP,EAAE,MAAO,cAAc,CACxD,CAAA,CAAQ,OAAO2B,EAAK,cAAe,EAAE,EAAGmO,EAAO9P,EAAE,OAAQ,QAAS,OAAOoQ,CAAK,CAAC,CAAC,CAClF,CAGA,SAASC,GAAW,EAA2B,CAC7C,IAAMC,EAAM/T,EAAK,YAAYyD,EAAE,SAAU,YAAY,CAAC,EACtDsQ,EAAI,aAAa,gBAAiB,OAAO,EACzC,IAAMC,EAAQD,EAAI,YAAYtQ,EAAE,OAAQ,OAAO,CAAC,EAChDuQ,EAAM,YAAY5O,EAAK,eAAgB,EAAE,CAAC,EAC1C2O,EAAI,YAAYV,GAAUhJ,EAAM,KAAK,CAAC,EACtC,IAAM4J,EAASF,EAAI,YAAYtQ,EAAE,OAAQ,QAAQ,CAAC,EAC5CyQ,GAAS,EAAe,EAAc,IAC1CD,EAAO,YAAYxQ,EAAE,OAAQoQ,EAAQnQ,EAAY,OAAQyQ,EAAON,CAAK,CAAC,EACxEK,EAAM7J,EAAM,MAAM,OAAQ,IAAK,OAAO,EACtC6J,EAAM7J,EAAM,QAAQ,OAAQ,IAAK,SAAS,EAE1C,IAAM+J,EAAQpU,EAAK,YAAYyD,EAAE,MAAO,cAAc,CAAC,EACvD,EAAM,OAAS,GACf,IAAM4Q,GAAY,EAAgB,EAAmB,IACnDlD,EAAK,MAAM,EAAG4B,EAAS,CAAC,CAAC,QAAS,GAAS,CAEzCqB,EADmB,YAAY3Q,EAAE,MAAO,eAAeC,GAAW,CAClE,CAAA,CAAK,OAAOD,EAAE,OAAQ,OAAQ0Q,CAAI,EAAG1Q,EAAE,OAAQ,OAAQE,EAAK,MAAM,EAAGqP,EAAe,CAAC,CAAC,CACxF,CAAC,EAEHqB,EAAShK,EAAM,MAAO,WAAY,GAAG,EACrCgK,EAAShK,EAAM,QAAS,aAAc,GAAG,EAEzC0J,EAAI,iBAAiB,YAAe,CAClC,EAAM,OAAS,CAACK,EAAM,OACtBL,EAAI,aAAa,gBAAiB,OAAO,CAACK,EAAM,MAAM,CAAC,EACvDJ,EAAM,gBAAgB5O,EAAKgP,EAAM,OAAS,eAAiB,cAAe,EAAE,CAAC,EACzE/J,EAAM,KAAI,SAAS,KAAOA,EAAM,GACtC,CAAC,CACH,CAGA,SAASiK,GAAS,EAAe,EAAmB,EAAoB,CAEtEpB,EADiB,YAAYzP,EAAE,MAAO,cAAcC,GAAW,CAC/D,CAAA,CAAI,OAAOD,EAAE,OAAQ,OAAQ0Q,CAAI,EAAGd,GAAUE,CAAK,CAAC,CACtD,CAEA,SAASgB,IAAoB,CAC3B,EAAK,OAAS,GACd,IAAMC,EAAQ/Q,EAAE,MAAO,eAAe,EAChCgR,EAAShR,EAAE,SAAU,gBAAgB,EAC3CgR,EAAO,OAAOrP,EAAK,MAAM,EAAG,eAAe,EAC3CqP,EAAO,iBAAiB,QAASrB,EAAU,EAC3CoB,EAAM,OACJ/Q,EAAE,IAAK,GAAI,kBAAkB,EAC7BA,EAAE,OAAQ,OAAQ,kEAAkE,EACpFgR,CACF,EACAvB,EAAK,gBAAgBsB,CAAK,CAC5B,CAEA,SAASE,GAAY,EAAwB,CAC3C,EAAK,OAAS,GACdzB,EAAK,gBAAgBO,GAAWG,EAAU,EAAI,CAAC,EAC/C,IAAMgB,EAAQlR,EAAE,MAAO,OAAO,EAC9BkR,EAAM,aAAa,OAAQ,OAAO,EAClC,IAAMhR,EAAOF,EAAE,KAAK,EACpBE,EAAK,OACHF,EAAE,IAAK,GAAI,eAAe,EAC1BA,EAAE,OAAQ,OAAQ,uFAAuF,CAC3G,EACAkR,EAAM,OAAOvP,EAAK,SAAS,EAAGzB,CAAI,EAClCuP,EAAK,gBAAgByB,CAAK,CAC5B,CAEA,SAASC,GAAc,EAAkB,EAAmB,EAA2B,CACrF,IAAMC,EAAO1C,GAAYK,EAAOD,CAAO,EACvC,EAAK,OAAS,GACd,IAAMuC,EAAQrR,EAAE,MAAO,OAAO,EAC9BqR,EAAM,OAAOrR,EAAE,OAAQ,GAAI+O,EAAM,QAAQ,EAAGpN,EAAK,QAAS,EAAE,EAAG3B,EAAE,OAAQ,GAAI8O,EAAQ,QAAQ,CAAC,EAC9FU,EAAK,gBAAgBO,GAAWG,EAAU,EAAK,EAAGmB,CAAK,EAEvD5B,EAAK,gBAAgB,EACjB2B,EAAK,QAAQ,QAAQjB,GAAa,UAAWiB,EAAK,QAAQ,MAAM,EACpEA,EAAK,QAAQ,QAAQf,EAAU,EAC3Be,EAAK,MAAM,QAAQjB,GAAa,QAASiB,EAAK,MAAM,MAAM,EAC9DA,EAAK,MAAM,QAAS,GAAUP,GAASf,EAAO,WAAY,GAAG,CAAC,EAC1DsB,EAAK,QAAQ,QAAQjB,GAAa,UAAWiB,EAAK,QAAQ,MAAM,EACpEA,EAAK,QAAQ,QAAS,GAAUP,GAASf,EAAO,aAAc,GAAG,CAAC,EAC7DvT,EAAK,mBACRkT,EAAK,YAAYzP,EAAE,MAAO,eAAe,CAAC,CAAC,CAAC,YAAYA,EAAE,OAAQ,OAAQ,mBAAmB,CAAC,CAElG,CAEA,IAAasR,GAAkB,CAC7B,MAAM,EAAO,CACX,EAAOpC,EAAKC,EAAO,eAAe,EAClC,EAAOC,EAAQD,EAAO,SAAS,EAC/B,EAAYD,EAAuBC,EAAO,oBAAoB,EAC9D,EAAKA,EAAO,0BAA0B,CAAC,CAAC,OAAS,CAAChB,GAAc,EAEhEuB,EAAU,iBAAiB,SAAU,SAAY,CAC/C,IAAMf,EAAOe,EAAU,QAAQ,GAE/B,GADA,EAAU,MAAQ,GACd,CAACf,EAAM,OACX,IAAMI,EAAQd,GAAY,MAAMU,EAAK,KAAK,CAAC,EACrCG,EAAUX,GAAc,EAC1BY,GAASD,EAASqC,GAAcxC,EAAK,KAAMI,EAAOD,CAAO,EACxDmC,GAAYtC,EAAK,IAAI,CAC5B,CAAC,EACDmC,GAAY,CACd,EAEA,QAAS,CAAC,CACZ,ECxJIS,EAAyC,KACvCC,GAAa,IAAI,IAMvB,SAASC,GAAe,EAA8B,CACpD,IAAIC,EAASF,GAAW,IAAIvT,CAAK,EACjC,GAAIyT,IAAW,IAAA,GAAW,CACxB,IAAU,SAAS,cAAc,QAAQ,CAAC,CAAC,WAAW,IAAI,EAG1D,EAAM,UAAY,OAClB,EAAM,UAAYzT,EAClB,IAAMkL,EAAQoI,EAAM,UACpB,EAAM,UAAY,OAClB,EAAM,UAAYtT,EAClB,EAASkL,IAAUoI,EAAM,UAAYpI,EAAQ,KAC7CqI,GAAW,IAAIvT,EAAOyT,CAAM,CAC9B,CACA,OAAOA,CACT,CAGA,SAAgBC,GAAgB,EAAyB,EAAyB,CAChF,IAAMC,EAASH,GAAeI,CAAK,EACnC,OAAO,MAAM,KAAKvO,CAAE,CAAC,CAClB,OAAQ,GAAS1E,EAAK,WAAW,IAAI,CAAC,CAAC,CACvC,OAAQ,GAAS6S,GAAenO,EAAG,iBAAiB1E,CAAI,CAAC,CAAC,KAAK,CAAC,IAAMgT,CAAM,CAAC,CAC7E,MAAM,EAAG,IAAM5M,EAAE,OAASC,EAAE,MAAM,CACvC,CAGA,SAAgB6M,GAAY,EAAuB,CACjD,IAAMzG,EAAQ,gDAAgD,KAAKpN,CAAK,EACxE,GAAI,CAACoN,EAAO,OAAOpN,EACnB,IAAM8T,EAAM1G,EACT,MAAM,EAAG,CAAC,CAAC,CACX,IAAK,GAAY,OAAO2G,CAAO,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,CAAC,CAAC,CAC/D,KAAK,EAAE,CAAC,CACR,YAAY,EACTC,EAAQ5G,EAAM,KAAO,IAAA,GAAY,GAAK,IAAI,KAAK,MAAM,OAAOA,EAAM,EAAE,EAAI,GAAG,EAAE,GACnF,MAAO,IAAM0G,EAAME,CACrB,CAGA,IAAaC,GAAgB,GAC3B,uCAAuC,KAAKjU,EAAM,KAAK,CAAC,EChD7CkU,GAAmD,CAC9D,CACE,MAAO,qBACP,MAAO,CACL,UACA,WACA,MACA,QACA,SACA,OACA,UACA,aACA,WACA,aACA,YACF,CACF,EACA,CACE,MAAO,YACP,MAAO,CACL,QACA,SACA,YACA,YACA,aACA,aACA,aACA,aACA,eACA,gBACA,cACA,mBACA,qBACA,sBACA,oBACA,eACA,eACA,gBACA,cACA,gBACA,iBACA,cACF,CACF,EACA,CACE,MAAO,mBACP,MAAO,CACL,iBACA,YACA,kBACA,cACA,gBACA,MACA,UACA,YACF,CACF,EACA,CACE,MAAO,cACP,MAAO,CAAC,OAAQ,YAAa,cAAe,aAAc,aAAc,eAAgB,OAAO,CACjG,EACA,CACE,MAAO,mBACP,MAAO,CACL,wBACA,qBACA,sBACA,oBACA,iBACA,gBACF,CACF,EACA,CAAE,MAAO,cAAe,MAAO,CAAC,cAAe,WAAY,WAAW,CAAE,EACxE,CACE,MAAO,aACP,MAAO,CACL,cACA,YACA,cACA,aACA,cACA,iBACA,aACA,iBACA,kBACA,cACA,OACF,CACF,EACA,CACE,MAAO,aACP,MAAO,CACL,mBACA,mBACA,kBACA,sBACA,mBACF,CACF,EACA,CACE,MAAO,yBACP,MAAO,CAAC,YAAa,mBAAoB,aAAc,YAAa,UAAW,gBAAgB,CACjG,CACF,EAGaC,GAAyC,CACpD,SAAU,SACV,IAAK,OACL,MAAO,OACP,OAAQ,OACR,KAAM,OACN,UAAW,OACX,SAAU,UACV,aAAc,UACd,aAAc,UACd,WAAY,UACZ,iBAAkB,MAClB,YAAa,SACb,kBAAmB,SACnB,cAAe,SACf,gBAAiB,SACjB,YAAa,IACb,cAAe,IACf,aAAc,OACd,aAAc,OACd,eAAgB,OAChB,MAAO,IACP,wBAAyB,OACzB,qBAAsB,OACtB,sBAAuB,OACvB,oBAAqB,OACrB,iBAAkB,OAClB,iBAAkB,MAClB,cAAe,OACf,WAAY,OACZ,YAAa,OACb,UAAW,OACX,UAAW,OACX,iBAAkB,OAClB,kBAAmB,0BACnB,iBAAkB,OAClB,aAAc,SACd,iBAAkB,SAClB,mBAAoB,MACtB,EC5GI5V,EAEA8V,GAAmB,GAIvB,SAASC,GAAY,EAAuB,CAC1C,MAAO,CACL,CAAE,KAAM,MAAO,MAAOnV,EAAG,SAAU,EACnC,CAAE,KAAM,KAAM,MAAOA,EAAG,GAAK,IAAMA,EAAG,GAAK,GAAI,EAC/C,CAAE,KAAM,UAAW,MAAO,MAAM,KAAKA,EAAG,SAAS,CAAC,CAAC,KAAK,GAAG,GAAK,GAAI,CACtE,CACF,CAEA,SAASoV,GAAS,EAAuB,CACvC,IAAMjS,EAAOnD,EAAG,sBAAsB,EACtC,MAAQ,CAAC,MAAO,QAAS,SAAU,OAAQ,QAAS,QAAQ,CAAC,CAAW,IAAK,IAAU,CACrF,OACA,MAAOmD,EAAKqB,EAAK,CAAC,QAAQ,CAAC,EAAI,IACjC,EAAE,CACJ,CAEA,SAAS6Q,GAAa,EAAmC,CACvD,OAAO,MAAM,KAAKnP,CAAE,CAAC,CAClB,OAAQ,GAAS1E,EAAK,WAAW,IAAI,CAAC,CAAC,CACvC,IAAK,IAAU,CAAE,OAAM,MAAO0E,EAAG,iBAAiB1B,CAAI,CAAC,CAAC,KAAK,CAAE,EAAE,CACtE,CAIA,SAAS8Q,GAAS,EAA2D,CAC3E,IAAMC,EAAQ3S,EAAE,MAAO,KAAK,EAC5B2S,EAAM,UAAU,OAAO,gBAAiB,CAAC,CAACrC,EAAI,UAAU,EACxDqC,EAAM,UAAU,OAAO,YAAa,CAAC,CAACrC,EAAI,QAAU,CAACA,EAAI,UAAU,EACnEqC,EAAM,YAAY3S,EAAE,OAAQ,OAAQsQ,EAAI,IAAI,CAAC,EACzC4B,GAAa5B,EAAI,KAAK,GACxBqC,EAAM,YAAY3S,EAAE,OAAQ,QAAQ,CAAC,CAAC,CAAC,MAAM,YAAY,WAAYsQ,EAAI,KAAK,EAEhF,IAAMsC,EAAUD,EAAM,YAAY3S,EAAE,OAAQ,QAASsQ,EAAI,OAAS,GAAG,CAAC,EAEtE,MADA,GAAQ,MAAQA,EAAI,MACb,CAAE,QAAO,SAAQ,CAC1B,CAEA,IAAMuC,GAAe,GAA6BH,GAASpC,CAAG,CAAC,CAAC,MAOhE,SAASwC,GAAY,EAAa,EAAa,EAA+B,CAC5E,GAAM,CAAE,QAAO,WAAYJ,GAASpC,CAAG,EACnCyC,EAAQzC,EAAI,MACZ0C,EAAa,CAAC,CAAC1C,EAAI,WACnB2C,EAAS,GAEb,EAAQ,gBAAkB,OAC1B,EAAQ,WAAa,GACrB,EAAQ,QAAQ,KAAO3C,EAAI,OAAS,UAAY,IAAMA,EAAI,KAG1D,IAAM4C,EAAU,GAAkB,CAC5BjV,GACFQ,GAAUD,EAAU8R,EAAI,KAAMrS,CAAK,EACnCY,GAAWzB,EAAIkT,EAAI,KAAMrS,CAAK,EAC9B,EAAa,GACb,EAAQA,GACC+U,IACTvU,GAAUD,EAAU8R,EAAI,KAAM,IAAI,EAClCtR,GAAa5B,EAAIkT,EAAI,IAAI,EAE7B,EAqCA,GAnCAsC,EAAQ,iBAAiB,YAAe,CACjCG,IAAO,EAAQ,YAAc,GACpC,CAAC,EACDH,EAAQ,iBAAiB,OAAS,GAAM,CACtC,IAAMO,GAASP,EAAQ,aAAe,GAAA,CAAI,KAAK,EAC/C,GAAIO,IAAUJ,GAAS,CAACE,EAAQ,CAC9B,EAAQ,YAAcF,GAAS,IAC/B,MACF,CACII,IAAUJ,GAAOG,EAAOC,CAAK,EACjC,IAAMC,EAAO,EAAE,cACf,GAAmBA,aAAgB,YAAeA,EAAK,QAAQ,KAAO,GAAM,GAC5E9V,EAAS,CACX,CAAC,EACDsV,EAAQ,iBAAiB,UAAY,GAAM,CACrC,EAAE,MAAQ,UACZ,EAAE,eAAe,EACjBA,EAAQ,KAAK,GAEX,EAAE,MAAQ,WACZ,EAAQ,YAAcG,EACtBH,EAAQ,KAAK,GAEf,IAAMS,EAAS,qBAAqB,KAAKT,EAAQ,aAAe,EAAE,EAClE,GAAIS,IAAW,EAAE,MAAQ,WAAa,EAAE,MAAQ,aAAc,CAC5D,EAAE,eAAe,EACjB,IAAMC,EAAO,EAAE,SAAW,GAAK,EAAE,OAAS,GAAM,EAC1CF,EAAO,WAAWC,EAAO,EAAE,GAAK,EAAE,MAAQ,UAAYC,EAAO,CAACA,GAC9DC,EAAWD,IAAS,IAAOD,EAAO,EAAE,CAAC,SAAS,GAAG,EAAI,EAAI,EAC/D,EAAQ,YAAcD,EAAK,QAAQG,CAAQ,EAAIF,EAAO,GACtDH,EAAON,EAAQ,WAAW,EAC1B,EAAS,EACX,CACF,CAAC,EAEGI,EAAY,CACd,IAAMQ,EAAQb,EAAM,YAAY3S,EAAE,SAAU,QAAS,GAAG,CAAC,EACzD,EAAM,MAAQ,gCACdwT,EAAM,iBAAiB,YAAe,CACpCN,EAAO,EAAE,EACT5V,EAAS,CACX,CAAC,CACH,CACA,OAAOqV,CACT,CAKA,IAAMc,GAAY,IAAI,IACpB,wIAAwI,MACtI,GACF,CACF,EAGA,SAASC,GAAc,EAAa,EAA+B,CACjE,GAAI,CAACvU,EAAS/B,CAAE,GAAK,CAACqW,GAAU,IAAIrW,EAAG,SAAS,EAAG,CACjD,IAAMuW,EAAO3T,EAAE,UAAW,OAAO,EAEjC,OADA2T,EAAK,YAAY3T,EAAE,OAAQ,OAAQ,GAAG5C,EAAG,UAAU,4DAA4D,CAAC,EACzGuW,CACT,CAEA,IAAMC,EAAU5T,EAAE,UAAW,eAAe,EAC5C4T,EAAQ,YAAY5T,EAAE,MAAO,UAAWb,EAAS/B,CAAE,EAAI,QAAU,SAAS,CAAC,EAC3E,IAAMyW,EAASD,EAAQ,YAAY5T,EAAE,MAAO,SAAUZ,GAAWhC,CAAE,CAAC,CAAC,EACrE,EAAO,gBAAkB,OACzB,EAAO,WAAa,GACpB,IAAM0W,EAASF,EAAQ,YAAY5T,EAAE,MAAO,QAAQ,CAAC,EACrD8T,EAAO,YAAY9T,EAAE,OAAQ,OAAQ,qCAAqC,CAAC,EAC3E,IAAMwT,EAAQM,EAAO,YAAY9T,EAAE,SAAU,QAAS,SAAS,CAAC,EAsBhE,MArBA,GAAM,OAAS,CAACT,GAAgBf,CAAQ,EAExCqV,EAAO,iBAAiB,UAAY,GAAM,CACxC,GAAIlJ,EAAE,MAAQ,UAAYA,EAAE,SAAWA,EAAE,SAAU,CACjDA,EAAE,eAAe,EACjB,IAAMrL,EAAUuU,EAAO,aAAe,GACtCpU,GAAarC,EAAIkC,CAAO,EACxBE,GAAYhB,EAAUc,CAAO,EAC7B,EAAM,OAAS,GACfuU,EAAO,KAAK,CACd,CACIlJ,EAAE,MAAQ,WACZ,EAAO,YAAcvL,GAAWhC,CAAE,EAClCyW,EAAO,KAAK,EAEhB,CAAC,EACDL,EAAM,iBAAiB,YAAe,CACpChU,GAAYhB,EAAU,IAAI,EAC1BkB,GAAetC,CAAE,EACjBE,EAAS,CACX,CAAC,EACMsW,CACT,CAIA,SAASG,GAAU,EAAa,EAA+B,CAE7D,IAAMC,EAAY9W,IAAW,SAAW,EAAIV,EAAK,UAC3CgC,EAAW4M,GAAYhO,CAAE,EACzB6W,EAAQ1V,GAAYC,CAAQ,EAC5B8E,EAAK,iBAAiBlG,CAAE,EACxB8W,GAAc,EAAe,EAAqB,EAAa,KAAO,CACrEC,EAAK,SACV9B,EAAK,YAAYrS,EAAE,MAAO,WAAWoU,IAActE,CAAK,CAAC,EACzDuC,EAAK,OAAO,GAAG8B,CAAI,EACrB,EACME,EAAiB,GACrB,CAACrI,EAAQ,QAAUsE,EAAI,KAAK,YAAY,CAAC,CAAC,SAAStE,EAAQ,MAAM,EAC7DsI,EAAY,GAAgBxB,GAAYxC,EAAKlT,EAAIoB,CAAQ,EAE/D6T,EAAK,gBAAgBqB,GAActW,EAAIoB,CAAQ,CAAC,EAEhD,IAAM+V,EAAS,OAAO,QAAQN,CAAK,CAAC,CAAC,KAAK,CAACrS,EAAM3D,MAAY,CAC3D,OACA,QACA,WAAY,GACZ,OAAQ,EACV,EAAE,EACFiW,EAAW,aAAcK,EAAO,IAAID,CAAQ,EAAG,WAAW,EACtDC,EAAO,QAAQlC,EAAK,YAAYrS,EAAE,MAAO,SAAS,CAAC,EAEvDkU,EAAW,UAAW3B,GAAYnV,CAAE,CAAC,CAAC,OAAOiX,CAAa,CAAC,CAAC,IAAIxB,EAAW,CAAC,EAC5EqB,EAAW,OAAQ1B,GAASpV,CAAE,CAAC,CAAC,OAAOiX,CAAa,CAAC,CAAC,IAAIxB,EAAW,CAAC,EACtE,IAAK,IAAM2B,KAASrC,GAAY,CAC9B,IAAMgC,EAAOK,EAAM,MAChB,IAAK,GAAiB,CACrB,IAAMC,EAAWnR,EAAG,iBAAiB1B,CAAI,CAAC,CAAC,KAAK,EAC1CoR,EAAaiB,EAAMrS,KAAU,IAAA,GACnC,MAAO,CACL,OACA,MAAOoR,EAAaiB,EAAMrS,GAAQ6S,EAClC,OAAQrC,GAAexQ,KAAU6S,EACjC,YACF,CACF,CAAC,CAAC,CACD,OAAQ,GAAQnE,EAAI,OAAS+D,EAAc/D,CAAG,CAAC,CAAC,CAChD,OAAQ,GAAQ,EAAEtE,EAAQ,cAAgBsE,EAAI,QAAU,CAACA,EAAI,WAAW,EAC3E4D,EAAWM,EAAM,MAAOL,EAAK,IAAIG,CAAQ,CAAC,CAC5C,CAIA,GAHAJ,EAAW,gBAAiBzB,GAAanP,CAAE,CAAC,CAAC,OAAO+Q,CAAa,CAAC,CAAC,IAAIC,CAAQ,CAAC,EAEhF,EAAK,UAAYN,EACb1B,GAAkB,CACpB,IAAMhC,EAAM,IAAI,OAAOgC,EAAgB,EACvC,GAAmB,GAEnB,eAAiB9V,EAAK,cAA2B,cAAc8T,EAAI,GAAG,CAAC,EAAE,MAAM,CAAC,CAClF,CACF,CAEA,SAASoE,GAAc,EAA0B,CAC/C,IAAMC,EAASzF,EAAuBC,EAAO,eAAe,EACtDyF,EAAa,GAAkB,CACnC,EAAO,MAAQC,EACf,EAAQ,OAASA,EAAM,YAAY,CAAC,CAAC,KAAK,EAC1CvX,EAAS,CACX,EACAqX,EAAO,iBAAiB,YAAeC,EAAUD,EAAO,KAAK,CAAC,EAC9DA,EAAO,iBAAiB,UAAY,GAAM,CACpC,EAAE,MAAQ,UAAUC,EAAU,EAAE,CACtC,CAAC,EACD1F,EAAKC,EAAO,QAAQ,CAAC,CAAC,iBAAiB,YAAeyF,EAAU,EAAE,CAAC,EAEnE1F,EAAuBC,EAAO,8BAA8B,CAAC,CAAC,iBAAiB,SAAW,GAAM,CAC9F,EAAQ,aAAgB,EAAE,OAA4B,QACtD7R,EAAS,CACX,CAAC,EACD4R,EAAuBC,EAAO,iCAAiC,CAAC,CAAC,iBAAiB,SAAW,GAAM,CACjG,EAAQ,gBAAmB,EAAE,OAA4B,QACzDpC,GAAa,CACf,CAAC,CACH,CAEA,IAAa+H,GAAc,CACzB,MAAM,EAAO,CACX,EAAO1F,EAAQD,EAAO,KAAK,EAC3BuF,GAAcvF,CAAK,CACrB,EACA,OAAQ4E,EACV,ECvRMgB,GAAa,IAAI,IAAI,CAAC,SAAU,QAAS,OAAQ,OAAQ,UAAU,CAAC,EAEtEtY,GAEEwY,GAAiB,GACrB,MAAM,KAAKrS,EAAK,QAAQ,CAAC,CAAC,OAAQ,GAAU,CAACE,EAAM8B,CAAK,GAAK,CAACmQ,GAAW,IAAInQ,EAAM,SAAS,CAAC,EAG/F,SAASsQ,GAAS,EAAe,EAAe,EAAoB,EAA+B,CACjG,IAAMC,EAAOnV,EAAE,KAAK,EACdoV,EAAWH,GAAcrS,CAAI,EAC7B0N,EAAM6E,EAAK,YAAYnV,EAAE,MAAO,OAAO,CAAC,EAC9CsQ,EAAI,aAAa,OAAQ,UAAU,EACnCA,EAAI,UAAU,OAAO,aAAc1N,IAASkM,CAAO,EACnDwB,EAAI,MAAM,YAAY,UAAW,OAAOpJ,CAAK,CAAC,EAC9C,IAAMqJ,EAAQD,EAAI,YAAYtQ,EAAE,OAAQ,OAAO,CAAC,EAChDsQ,EAAI,YAAYpH,GAActG,EAAM,YAAY,CAAC,EACjD,IAAMyS,EAAYF,EAAK,YAAYnV,EAAE,KAAK,CAAC,EAEvCsV,EAAS,GACPC,EAAW,GAAkB,CACjC,EAASnC,EACT7C,EAAM,gBAAgB5O,EAAK2T,EAAS,cAAgB,eAAgB,EAAE,CAAC,EACvED,EAAU,gBACR,GAAIC,EAASF,EAAS,IAAK,GAAUF,GAAStQ,EAAOsC,EAAQ,EAAGsO,EAAM1G,CAAO,CAAC,EAAI,CAAC,CACrF,CACF,EAYA,OAXIsG,EAAS,QAAQG,EAAQC,EAAK,IAAI5S,CAAI,CAAC,EAE3C2N,EAAM,iBAAiB,QAAU,GAAM,CACrC,EAAE,gBAAgB,EACd6E,EAAS,QAAQG,EAAQ,CAACD,CAAM,CACtC,CAAC,EACDhF,EAAI,iBAAiB,iBAAoBtD,GAAiBpK,CAAI,CAAC,EAC/D0N,EAAI,iBAAiB,aAAcrD,EAAgB,EACnDqD,EAAI,iBAAiB,YAAe,CAC9B1N,IAASkM,GAAS3R,EAAIyF,CAAI,CAChC,CAAC,EACMuS,CACT,CAEA,SAASM,GAAa,EAAmB,CACvC,IAAM5V,EAAO6G,GAAQtJ,CAAE,GAAK,SAAS,KAC/BoY,EAAO,IAAI,IACjB,IAAK,IAAI5S,EAAuBxF,EAAIwF,IAE9BA,IAASxF,GAAIoY,EAAK,IAAI5S,CAAI,EAC1BA,IAAS/C,GAH2B,EAAO+C,EAAK,eAKtD,IAAMoR,EAAYvX,GAAK,UACvBuY,GAAK,gBAAgBE,GAASrV,EAAM,EAAG2V,EAAMpY,CAAE,CAAC,EAChD,GAAK,UAAY4W,CACnB,CAEA,IAAa0B,GAAiB,CAC5B,MAAM,EAAO,CACX,GAAOtG,EAAQD,EAAO,QAAQ,CAChC,EACA,OAAQsG,EACV,ECrEME,GAAa,gBAEbC,GAAU,CAAC,QAAS,MAAO,SAAU,SAAS,EAQpD,SAAgBC,GAAW,EAA0B,CACnD,IAAMC,EAAShY,EAAkB6X,EAAU,EAC3C,EAAM,QAAQ,IAAMG,EAAO,KAAOF,GAAQ,SAASE,EAAO,GAAG,EAAIA,EAAO,IAAM,QAC9E,EAAM,MAAM,MAAQA,EAAO,OAAS,GACpC,EAAQ,KAAOA,EAAO,OAAS,MAAQ,MAAQ,IACjD,CAEA,SAAgBC,GAAW,EAA0B,CACnD,IAAMP,EAAOrG,EAAM,QAAQ,KAAO,GAMlCnR,EAAW2X,GAAY,CAJrB,MAAOxG,EAAM,MAAM,MACnB,IAAKyG,GAAQ,SAASJ,CAAI,EAAIA,EAAQ1X,EAAkB6X,EAAU,CAAC,CAAC,KAAO,QAC3E,KAAM3J,EAAQ,IAEO,CAAM,CAC/B,CAEA,SAAgBgK,GAAO,EAAoB,EAAmB,CAC5D,EAAM,QAAQ,IAAM3G,EACpB0G,GAAW5G,CAAK,CAClB,C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC1BMgH,GAA+B,CAAE,IAAKtL,GAAS,QAASC,EAAY,EAE7DsL,GAAoB,CAC/B,MAAM,EAAO,CACX,IAAMC,EAAOjH,EAAQD,EAAO,WAAW,EACvC,EAAK,UAAYzS,GAEjB,IAAK,IAAMqB,KAAOsY,EAAK,iBAA8B,eAAe,EAClE,EAAI,YAAcF,GAAKpY,EAAI,QAAQ,KAAO,KAAO,EAErD,EAEA,QAAS,CAAC,CACZ,EClBA,SAAgBuY,GAAgB,EAA2C,CACzE,IAAMC,EAAQ,GAA6B,CACzC,IAAK,IAAMC,KAAQC,EACb,aAAcD,GAAMD,EAAKC,EAAK,QAAuB,EACrDA,aAAgB,cAAcE,EAAMF,CAAI,CAEhD,EACA,IAAK,IAAMG,KAAS,SAAS,YAC3B,GAAI,CACFJ,EAAKI,EAAM,QAAQ,CACrB,MAAQ,CAER,CAEJ,CAMA,SAASC,GAAc,EAAoC,CACzD,IAAMC,EAAgC,CAAC,EASvC,OARAP,GAAiB,GAAS,CACxB,GAAI,CACElZ,EAAG,QAAQoZ,EAAK,YAAY,GAAGK,EAAO,KAAKL,EAAK,KAAK,CAC3D,MAAQ,CAER,CACF,CAAC,EACDK,EAAO,KAAMzZ,EAAmB,KAAK,EAC9ByZ,CACT,CAKA,SAAgBC,IAAiC,CAC/C,IAAMC,EAAQ,IAAI,IAClB,MAAQ,IAAO,CACb,IAAIF,EAASE,EAAM,IAAI3Z,CAAE,EAEzB,OADKyZ,GAAQE,EAAM,IAAI3Z,EAAK,EAASwZ,GAAcxZ,CAAE,CAAE,EAChDyZ,CACT,CACF,CAOA,SAAgBG,GAAc,EAA+B,EAAsB,CACjF,IAAMlY,EAAQ+X,EAAO,SAAU,GAAcI,EAAU,iBAAiBrY,CAAI,CAAC,EAC7E,OAAOE,EAAQA,EAAM,iBAAiBF,CAAI,CAAC,CAAC,KAAK,EAAI,EACvD,CC5CA,IAAMsY,GAA2C,CAC/C,OAAQ,CAAC,EAAG,EAAG,EAAG,CAAC,EACnB,KAAM,CAAC,IAAM,GAAK,IAAM,CAAC,EACzB,UAAW,CAAC,IAAM,EAAG,EAAG,CAAC,EACzB,WAAY,CAAC,EAAG,EAAG,IAAM,CAAC,EAC1B,cAAe,CAAC,IAAM,EAAG,IAAM,CAAC,CAClC,EAGA,SAAgBC,GAAY,EAA0B,CACpD,IAAMC,EAAU,yBAAyB,KAAKC,CAAM,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,EAChF,OAAOD,GAAS,SAAW,EAAIA,EAAWF,GAAeG,IAAWH,GAAe,MACrF,CAGA,IAAMI,GAAa,GAA4BrZ,EAAM,MAAM,iBAAiB,EAG5E,SAASsZ,GAAI,EAAoB,EAAmB,EAA+B,CACjF,IAAMC,EAAeF,GAAUG,CAAS,EAClCC,EAAaJ,GAAUK,CAAO,EACpC,OAAOL,GAAUM,CAAU,CAAC,CACzB,KAAK,EAAU,IAAM,CACpB,IAAMP,EAASK,EAAWxS,EAAIwS,EAAW,QACzC,MAAO,CACL,WACA,SAAUF,EAAatS,EAAIsS,EAAa,QACxC,SACA,MAAOL,GAAYE,CAAM,CAC3B,CACF,CAAC,CAAC,CACD,OAAQ,GAAS,QAAQ,KAAK1J,EAAK,QAAQ,CAAC,CACjD,CAEA,SAAgBkK,GAAY,EAAuC,CACjE,IAAMC,EAAcP,GAAIjU,EAAG,mBAAoBA,EAAG,mBAAoBA,EAAG,wBAAwB,EAC3FyU,EACJzU,EAAG,gBAAkB,OACjB,CAAC,EACDiU,GAAIjU,EAAG,cAAeA,EAAG,kBAAmBA,EAAG,uBAAuB,EAC5E,MAAO,CAAC,GAAGwU,EAAa,GAAGC,CAAU,CACvC,CC/CA,SAAgBC,GACd,EACA,EACA,EACQ,CACR,IAAMlX,EAAS1D,EAAG,cAAgB,iBAAiBA,EAAG,aAAa,EAAI,KACjE6a,EAASnX,IAAW,MAAQA,EAAO,QAAQ,SAAS,MAAM,EAC1DoX,EAAYD,GAAUnX,EAAO,cAAc,WAAW,KAAK,EAC3DqX,EAAQ7U,EAAG,WAAa,IACxB8U,EAAYtX,GAAUwC,EAAG,YAAc,OAASxC,EAAO,WAAawC,EAAG,UACvE+U,EAAYD,IAAc,UAAYA,IAAc,UAEpDtW,GAAU,EAAc,EAAqB,IAAoC,CACrF,IAAMwW,EAAWtB,GAAcH,EAAQjY,CAAI,EAI3C,OAHI0Z,IAAa,OAAe,OAC5BA,GAAYA,IAAa,QAAU,CAACA,EAAS,SAAS,SAAS,EAAU,QACzEL,GAAgBM,EAAaJ,EAAQE,GAAa,OAAS,MACxDG,EAAiB,OAAS,KACnC,EAMA,MAAO,GAFO1W,EAAO,QAASoW,EADX,CAAC,QAAS,OAAQ,OAAQ,YAAa,OAAO,CAAC,CAAC,SAAS5U,EAAG,OACtC,CAE/B,EAAM,KADDxB,EAAO,SAAUmW,GAAU,CAACC,EAAW,EACjC,GACvB,CC1BA,SAAgBO,GAAa,CAAC9X,EAAKC,EAAOC,EAAQH,GAAyB,CAGzE,OAFIC,IAAQC,GAASD,IAAQE,GAAUF,IAAQD,EAAaC,EACxDA,IAAQE,GAAUD,IAAUF,EAAa,GAAGC,EAAI,GAAGC,IAChD,GAAGD,EAAI,GAAGC,EAAM,GAAGC,EAAO,GAAGH,GACtC,CAMA,SAAgBgY,GAAY,EAAe,EAAY,EAA8B,CACnF,OAAOza,EAAM,QAAQ,oBAAqB,EAAG,IAC3C0a,IAAS,KAAO5S,EAAS,GAAG,KAAK,MAAO,OAAOA,CAAM,EAAI6S,EAAgB,GAAI,EAAI,IAAK,IACxF,CACF,CAMA,SAAgBC,GACd,EACA,EACA,EACA,EACQ,CAER,OADIF,IAAS,MAAcD,GAAYza,EAAO,MAAO2a,CAAY,EAC1DE,IAAW,KAAOJ,GAAYza,EAAO,KAAM2a,CAAY,EAAI3a,CACpE,CAMA,SAAgB8a,GACd,EACA,EACA,EACA,EACA,EACQ,CAGR,OAFID,IAAW,MAAQH,IAAS,KAAaK,GAAUpX,EAAM4C,CAAK,EAE3D,GAAG5C,EAAK,IAAI6W,GADHjU,EAAM,IAAK,GAASqU,GAAa,GAAGxV,EAAK,IAAKsV,EAAMC,EAAc,KAAK,CACvD,CAAO,GACzC,CAGA,SAAgBI,GAAU,EAA4B,EAAyB,CAC7E,GAAM,CAACrY,EAAKC,EAAOC,EAAQH,GAAQ8D,EAC7BzG,EAAM6D,EAAK,GAGjB,OAFIjB,IAAQC,GAASD,IAAQE,GAAUF,IAAQD,EAAa,GAAG3C,EAAI,IAAI4C,IACnEA,IAAQE,GAAUD,IAAUF,EAAa,GAAG3C,EAAI,KAAK4C,EAAI,IAAI5C,EAAI,KAAK6C,IACnE,GAAG7C,EAAI,KAAK4C,EAAI,IAAI5C,EAAI,KAAK6C,EAAM,IAAI7C,EAAI,KAAK8C,EAAO,IAAI9C,EAAI,KAAK2C,GAC7E,CC5BA,IAAMuY,GAAc,mBAGdC,GAAU,EAAe,GAAGC,IAChCA,EAAS,SAASlb,CAAK,EAAI,GAAKA,EAG5Bmb,GAAa,GAA2B,CAACxX,EAAM,GAAGqB,EAAM,IAAK,GAAS,GAAGrB,EAAK,GAAGyB,GAAM,CAAC,EAG9F,SAASgW,GAAU,EAAyB,EAAmD,CAC7F,IAAM7U,EAAQtB,EAAQI,EAAIF,CAAQ,EAClC,OAAOqV,GAAaxV,EAAM,IAAK,GAAS,GAAGuB,EAAMnB,GAAM,GAAG,CAAC,CAC7D,CAEA,SAASiW,GAAW,EAAa,EAAyB,EAA+B,CACvF,IAAM/Y,EAAOnD,EAAG,sBAAsB,EAChCoE,EAAON,GAAW9D,EAAImD,CAAI,EAC1BgZ,EAAU5S,EAAcvJ,EAAImD,CAAI,EAChCiZ,EAASlW,EAAG,QAAQ,SAAS,MAAM,EACnCmW,EAASnW,EAAG,QAAQ,SAAS,MAAM,EAEnC6Q,EAAe,CACnB,CACE,KAAM,OACN,MAAO,GAAG3S,EAAK,MAAM,KAAKA,EAAK,SAC/B,MAAO,CAAC,QAAS,QAAQ,EACzB,KAAMwW,GAAa5a,EAAIkG,EAAIoW,EAAStc,CAAE,CAAC,CACzC,EACA,CAAE,KAAM,WAAY,MAAOmc,GAAS,MAAQ,GAAI,KAAMA,GAAS,KAAM,EACrE,CAAE,KAAM,UAAW,MAAOL,EAAOG,GAAU/V,EAAI,SAAS,EAAG,KAAK,EAAG,MAAO8V,GAAU,SAAS,CAAE,EAC/F,CAAE,KAAM,SAAU,MAAOF,EAAOG,GAAU/V,EAAI,QAAQ,EAAG,KAAK,EAAG,MAAO8V,GAAU,QAAQ,CAAE,CAC9F,EACA,GAAII,EAAQ,CACV,IAAMrE,EAAO7R,EAAG,WAAa,SAAW,GAAK,QAC7C6Q,EAAK,KACH,CAAE,KAAM,SAAU,MAAO,QAAQ7Q,EAAG,gBAAgB6R,GAAO,EAC3D,CAAE,KAAM,QAAS,MAAO+D,EAAO5V,EAAG,WAAY,QAAQ,CAAE,EACxD,CAAE,KAAM,UAAW,MAAO4V,EAAO5V,EAAG,eAAgB,QAAQ,CAAE,CAChE,CACF,CAcA,GAbImW,GACFtF,EAAK,KACH,CAAE,KAAM,SAAU,MAAO,MAAO,EAChC,CAAE,KAAM,UAAW,MAAO+E,EAAO5V,EAAG,oBAAqB,MAAM,CAAE,CACnE,GAEEkW,GAAUC,IACZtF,EAAK,KAAK,CACR,KAAM,MACN,MAAO+E,EAAO5V,EAAG,IAAK,SAAU,KAAK,EACrC,MAAO,CAAC,MAAO,UAAW,YAAY,CACxC,CAAC,EAECA,EAAG,WAAa,SAAU,CAC5B,IAAM2C,EAAShD,EAAM,OAAQ,GAASK,EAAG,iBAAiBD,CAAI,IAAM,MAAM,CAAC,CAAC,IACzE,GAAS,GAAGA,EAAK,GAAGC,EAAG,iBAAiBD,CAAI,GAC/C,EACA8Q,EAAK,KAAK,CAAE,KAAM,WAAY,MAAO,CAAC7Q,EAAG,SAAU,GAAG2C,CAAM,CAAC,CAAC,KAAK,KAAK,CAAE,CAAC,CAC7E,CACA,OAAOkO,CACT,CAEA,SAASwF,GAAU,EAAiC,CAClD,IAAMC,EAAeP,GAAU/V,EAAI,QAAQ,EACrCuW,EAAYD,IAAiB,MAC7BE,EAASrB,GAAa,CAC1BnV,EAAG,oBACHA,EAAG,qBACHA,EAAG,wBACHA,EAAG,sBACL,CAAC,EACD,MAAO,CACL,CACE,KAAM,aACN,MAAO4V,EAAO5V,EAAG,gBAAiB2V,EAAW,EAC7C,MAAO,GACP,MAAO,CAAC,mBAAoB,YAAY,CAC1C,EACA,CACE,KAAM,WACN,MAAOC,EAAO5V,EAAG,gBAAiB,MAAM,EACxC,MAAO,CAAC,mBAAoB,YAAY,CAC1C,EACA,CACE,KAAM,SACN,MAAOuW,EAAY,GAAGD,EAAa,GAAGtW,EAAG,iBAAmB,GAC5D,MAAO,CAAC,eAAgB,SAAU,GAAG8V,GAAU,QAAQ,CAAC,CAC1D,EACA,CACE,KAAM,eACN,MAAOS,EAAYvW,EAAG,eAAiB,GACvC,MAAO,GACP,MAAO,CAAC,eAAgB,SAAU,GAAG8V,GAAU,QAAQ,EAAG,kBAAkB,CAC9E,EACA,CAAE,KAAM,SAAU,MAAOF,EAAOY,EAAQ,KAAK,EAAG,MAAO,CAAC,eAAe,CAAE,EACzE,CAAE,KAAM,SAAU,MAAOZ,EAAO5V,EAAG,UAAW,MAAM,EAAG,MAAO,CAAC,YAAY,CAAE,EAC7E,CAAE,KAAM,UAAW,MAAO4V,EAAO5V,EAAG,QAAS,GAAG,EAAG,MAAO,CAAC,SAAS,CAAE,CACxE,CACF,CAEA,SAASyW,GAAS,EAAa,EAAiC,CAM9D,OAJE5a,EAAS/B,CAAE,GACX,MAAM,KAAKA,EAAG,UAAU,CAAC,CAAC,KAAM,GAASwF,EAAK,WAAa,KAAK,WAAaA,EAAK,WAAW,KAAK,CAAC,EAG9F,CACL,CACE,KAAM,OACN,MAAO,GAJIU,EAAG,WAAW,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,IAAK,EAI7C,EAAO,GAAGA,EAAG,WAAW,GAAGA,EAAG,SAAS,GAAGA,EAAG,aACvD,MAAO,CAAC,OAAQ,cAAe,cAAe,YAAa,aAAa,CAC1E,EACA,CAAE,KAAM,QAAS,MAAOA,EAAG,MAAO,MAAO,GAAM,MAAO,CAAC,OAAO,CAAE,EAChE,CAAE,KAAM,UAAW,MAAO4V,EAAO5V,EAAG,cAAe,SAAU,KAAK,EAAG,MAAO,CAAC,gBAAgB,CAAE,EAC/F,CAAE,KAAM,YAAa,MAAO4V,EAAO5V,EAAG,cAAe,MAAM,CAAE,EAC7D,CAAE,KAAM,QAAS,MAAO4V,EAAO5V,EAAG,UAAW,QAAS,MAAM,CAAE,EAC9D,CAAE,KAAM,aAAc,MAAO4V,EAAO5V,EAAG,mBAAoB,MAAM,CAAE,CACrE,EAbqB,CAAC,CAcxB,CAEA,SAAS0W,GAAS,EAAa,EAAiC,CAE9D,OADM5c,aAAc,WACb,CACL,CAAE,KAAM,OAAQ,MAAO8b,EAAO5V,EAAG,KAAM,MAAM,EAAG,MAAO,GAAM,MAAO,CAAC,OAAQ,OAAO,CAAE,EACtF,CAAE,KAAM,SAAU,MAAO4V,EAAO5V,EAAG,OAAQ,MAAM,EAAG,MAAO,GAAM,MAAO,CAAC,SAAU,OAAO,CAAE,EAC5F,CACE,KAAM,eACN,MAAOA,EAAG,SAAW,OAAS,GAAKA,EAAG,YACtC,MAAO,CAAC,cAAc,CACxB,CACF,EATwC,CAAC,CAU3C,CAEA,SAAS2W,GAAW,EAAiC,CACnD,OAAOpC,GAAYvU,CAAE,CAAC,CAAC,IAAK,IAAU,CACpC,KAAMqK,EAAK,SACX,MAAO,GAAGA,EAAK,SAAS,GAAGA,EAAK,QAClC,EAAE,CACJ,CAEA,SAAgBuM,GACd,EACA,EACA,EACe,CACf,MAAO,CACL,CAAE,MAAO,SAAU,KAAMZ,GAAWlc,EAAIkG,EAAIoW,CAAQ,CAAE,EACtD,CAAE,MAAO,QAAS,KAAMC,GAAUrW,CAAE,CAAE,EACtC,CAAE,MAAO,OAAQ,KAAMyW,GAAS3c,EAAIkG,CAAE,CAAE,EACxC,CAAE,MAAO,OAAQ,KAAM0W,GAAS5c,EAAIkG,CAAE,CAAE,EACxC,CAAE,MAAO,SAAU,KAAM2W,GAAW3W,CAAE,CAAE,CAC1C,CACF,CAUA,SAAgB6W,GACd,EACA,EACsD,CACtD,IAAMC,EAAW,GACf,IAAI,IACFC,EAAS,QAAS,GAChBzG,EAAQ,KAAK,IAAK,GAAyB,CAAC,GAAGA,EAAQ,MAAM,GAAG0G,EAAK,OAAQA,CAAI,CAAC,CACpF,CACF,EACItL,EAASoL,EAAQG,CAAI,EACrBtL,EAAQmL,EAAQI,CAAK,EACrBC,EAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAGzL,EAAO,KAAK,EAAG,GAAGC,EAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAE3D,OAAQ,GAAQ,CAAClR,EAAI,SAAS,WAAW,CAAC,CAAC,CAC3C,OAAQ,GAAQiR,EAAO,IAAIjR,CAAG,CAAC,EAAE,OAASkR,EAAM,IAAIlR,CAAG,CAAC,EAAE,KAAK,EAE5D2c,EAAcD,EAAK,OADR,IAAiBzL,EAAO,IAAIjR,CAAG,CAAC,EAAE,OAAS,OAASkR,EAAM,IAAIlR,CAAG,CAAC,EAAE,OAAS,GACvD,CAAC,CAAC,IAAK,IAAS,CACrD,KAAMA,EAAI,MAAM,GAAG,CAAC,CAAC,GACrB,OAAQiR,EAAO,IAAIjR,CAAG,EACtB,MAAOkR,EAAM,IAAIlR,CAAG,CACtB,EAAE,EACF,MAAO,CAAE,cAAa,UAAW0c,EAAK,OAASC,EAAY,MAAO,CACpE,CCpNA,IAAMC,GAAY,IAAI,IAAI,CACxB,QACA,OACA,SACA,OACA,cACA,YACA,cACA,cACA,gBACF,CAAC,EAOD,SAAgBC,GAAe,EAAa,EAAiB,EAAiC,CAC5F,IAAMC,EAAWlc,EAAM,OAAS,GAAKA,EAAM,MAAO,GAASgc,GAAU,IAAI/b,CAAI,CAAC,EAC9E,IAAK,IAAIgE,EAAuBxF,EAAIwF,EAAM,EAAOiY,EAAWjY,EAAK,cAAgB,KAAM,CACrF,IAAMiU,EAAS6C,EAAS9W,CAAI,EACtBkY,EAASnc,EACZ,IAAK,GAASkY,EAAO,SAAU,GAAU/X,EAAM,iBAAiBF,CAAI,CAAC,CAAC,EAAE,iBAAiBA,CAAI,CAAC,CAAC,CAC/F,OAAQ,GAAUX,IAAU,IAAA,EAAS,EACxC,GAAI,CAAC6c,EAAO,OAAQ,SACpB,IAAMC,EAAQD,EAAO,QAAS,GAAU,CAAC,GAAG7c,EAAM,SAAS,qBAAqB,CAAC,CAAC,EAClF,MAAO,CAAC,GAAG,IAAI,IAAI8c,EAAM,IAAK,GAAUC,EAAM,EAAE,CAAC,CAAC,CACpD,CACA,MAAO,CAAC,CACV,CAOA,SAAgBC,GAAU,EAAe,EAA4B,CACnE,IAAMtd,EAASud,EAAS,KAAM,GAAcC,EAAM,WAAWlE,CAAS,CAAC,EACvE,OAAOtZ,EAASwd,EAAM,MAAMxd,EAAO,MAAM,CAAC,CAAC,WAAW,IAAK,GAAG,EAAI,EACpE,CAEA,IAAMyd,GAAe,IAAI,IAMzB,SAAgBC,GAAW,EAAiD,CAC1E,IAAIC,EAAQF,GAAa,IAAID,CAAK,EAClC,GAAI,CAACG,EAAO,CACV,IAAMC,EAAU,IAAI,IACpBjF,GAAiB,GAAS,CACxB,IAAMrY,EAAQuY,EAAK,MAAM,iBAAiB2E,CAAK,CAAC,CAAC,KAAK,EAClDld,GAAOsd,EAAQ,IAAI/E,EAAK,aAAcvY,CAAK,CACjD,CAAC,EACD,EAAQ,IAAI,IAAIsd,EAAQ,OAAO,CAAC,CAAC,CAAC,KAAO,EAAI,CAAC,GAAGA,CAAO,EAAI,CAAC,EAC7DH,GAAa,IAAID,EAAOG,CAAK,CAC/B,CACA,OAAOA,CACT,CAGA,IAAaE,GAAa,GACxB,MAAM,KAAKlY,CAAE,CAAC,CAAC,KAAM,GAAS1E,EAAK,WAAW,IAAI,CAAC,ECjE/C6c,GAAc,KAQpB,SAAgBC,EACd,EACA,EACA,EACG,CAeH,OAdA9Y,EAAK,UAAU,IAAI,MAAM,EACzB,EAAK,QAAU,gBACfA,EAAK,iBAAiB,QAAS,KAAO,IAAM,CAC1C+H,EAAE,gBAAgB,EAClB,IAAI+G,EAAS,YACb,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,OAAOxR,GAAS,WAAaA,EAAK,EAAIA,CAAI,CAChF,MAAQ,CACN,EAAS,iBACX,CACA,IAAMyb,EAASC,EAAa,CAAChZ,EAAMgZ,CAAU,EAAI,CAAChZ,CAAI,EACtD+Y,EAAO,QAAS,GAAOve,EAAG,UAAU,IAAIsU,CAAM,CAAC,EAC/C,eAAiBiK,EAAO,QAAS,GAAOve,EAAG,UAAU,OAAOsU,CAAM,CAAC,EAAG+J,EAAW,CACnF,CAAC,EACM7Y,CACT,CCCA,IAAI6E,EACA4O,EAEAwF,GAAkB,GAYhBjD,OACJ,WAAW,iBAAiB,SAAS,eAAe,CAAC,CAAC,QAAQ,GAAK,GAG/D7F,GAAS,GAA0B2F,GAAYza,EAAO+N,EAAQ,KAAM4M,GAAa,CAAC,EAGlFkD,GAAU,GACdjD,GAAa5a,EAAO+N,EAAQ,KAAM4M,GAAa,EAAGpb,EAAO,UAAU,EAE/Due,GAAa,GAA2B,KAAK,MAAMhW,EAAS,GAAG,EAAI,IAGnEiW,GAAa,GACjB1B,EAAK,MAAQxI,GAAYwI,EAAK,KAAK,EAAIvH,GAAMuH,EAAK,KAAK,EAEzD,SAAS2B,GAAO,EAAe,EAAQ,GAAoB,CACzD,IAAMC,EAAOlc,EAAE,OAAQmc,EAAQ,kBAAoB,QAAQ,EAE3D,OADAD,EAAK,MAAM,YAAY,WAAYrK,CAAK,EACjCqK,CACT,CAGA,SAASE,EAAM,EAAe,EAAO,GAAI,EAAY,GAAiB,CACpE,IAAMxI,EAAUyC,EAAK,YAAYrW,EAAE,UAAW,SAASC,GAAW,CAAC,EAC7DuP,EAAOoE,EAAQ,YAAY5T,EAAE,MAAO,YAAY,CAAC,EAGvD,OAFAwP,EAAK,YAAYxP,EAAE,KAAM,GAAI8P,CAAK,CAAC,EAC/B6D,GAAMnE,EAAK,YAAYxP,EAAE,OAAQ,OAAQ2T,CAAI,CAAC,EAC3CC,CACT,CAGA,SAASlH,GAAK,EAAe,EAAe,EAAO,GAAoB,CACrE,IAAM4D,EAAMtQ,EAAE,MAAOqc,EAAO,OAAS,MAAM,EAG3C,OAFA/L,EAAI,YAAYtQ,EAAE,OAAQ,QAAS4D,CAAK,CAAC,EACzC8X,EAAYpL,EAAI,YAAYtQ,EAAE,OAAQ,QAAS+S,GAAM9U,CAAK,CAAC,CAAC,EAAG6d,GAAO7d,CAAK,CAAC,EACrEqS,CACT,CAGA,IAAMgM,GAAY,CAAE,YAAqB,EAAiB,IACxDjC,EAAS,KAAM,GAAMkC,EAAE,QAAU3I,CAAO,CAAC,EAAE,KAAK,KAAM,GAAQtD,EAAI,OAAS1O,GAAQ0O,EAAI,KAAK,EAK9F,SAASkM,GAAe,EAA4B,CAClD,IAAMC,EAAO,wBAAwB,KAAKxc,CAAS,CAAC,GAAG,IAAM,GACvDyc,EAAY,QAAQ,KAAKD,CAAI,GAAK,QAAQ,KAAKA,CAAI,EACzD,MAAO,iBAAiB,KAAKxc,CAAS,GAAKyc,CAC7C,CAEA,SAASC,GAAa,EAAmB,CACvC,IAAMpc,EAAOnD,EAAG,sBAAsB,EAChCwJ,EAAQD,EAAcvJ,EAAImD,CAAI,CAAC,EAAE,MACjCO,EAAS1D,EAAG,cACZgM,EAAU,MAAM,KAAKhM,EAAG,SAAS,EACjCwf,EAAaxT,EAAQ,OAAQ,GAAS,CAACoT,GAAe5a,CAAI,CAAC,EACjE6F,EAAO,gBAAgB,EAGvB,IAAMoV,EAASpV,EAAO,YAAYzH,EAAE,MAAO,QAAQ,CAAC,EAC9C8c,MAAkBD,EAAO,YAAY7c,EAAE,OAAQ,MAAO,GAAG,CAAC,CAAC,CAAC,aAAa,cAAe,MAAM,EAKpG,GAJI4G,IACFiW,EAAO,YAAY7c,EAAE,OAAQ,QAAS4G,CAAK,CAAC,EAC5CkW,EAAU,GAERhc,GAAUA,IAAW,SAAS,KAAM,CACtC,IAAMic,EAAKF,EAAO,YAAY7c,EAAE,SAAU,QAAS8I,EAAYhI,CAAM,CAAC,CAAC,EACvE,EAAG,MAAQ,iBACXic,EAAG,iBAAiB,YAAe5f,EAAI2D,CAAM,CAAC,EAC9Cgc,EAAU,CACZ,CACAD,EAAO,YAAY7c,EAAE,OAAQ,mBAAoB8I,EAAY1L,CAAE,CAAC,CAAC,EAGjE,IAAM0S,EAAQrI,EAAO,YAAYzH,EAAE,MAAO,WAAW,CAAC,EACtD8P,EAAM,YAAY9P,EAAE,OAAQ,WAAY5C,EAAG,SAAS,CAAC,EACrD,IAAMwE,EAAOkH,EAAY1L,CAAE,EAM3B,GALA0S,EAAM,YAAY9P,EAAE,OAAQ,QAAS4B,IAASxE,EAAG,UAAawf,EAAW,IAAMhb,EAAQA,CAAI,CAAC,EAC5FkO,EAAM,YAAY9P,EAAE,OAAQ,QAAQ,CAAC,EACrC8P,EAAM,YAAY9P,EAAE,OAAQ,aAAc+I,EAAS3L,EAAImD,CAAI,CAAC,CAAC,EAGzD,CAAC6I,EAAQ,OAAQ,OACrB,IAAM4T,EAAShd,EAAE,SAAU,MAAM,EAKjC,GAJAgd,EAAO,iBAAiB,YAAe,CACrC,GAAkB,CAACnB,GACnBc,GAAavf,CAAE,CACjB,CAAC,EACGye,GAAiB,CACnB,IAAMnO,EAAOjG,EAAO,YAAYzH,EAAE,MAAO,YAAY,CAAC,EACtD,CAAC,GAAG4c,EAAY,GAAGxT,EAAQ,OAAOoT,EAAc,CAAC,CAAC,CAAC,QAAS,GAAc,CACxE,IAAMN,EAAOlc,EAAE,OAAQ,aAAc,IAAMC,CAAS,EACpDic,EAAK,UAAU,OAAO,eAAgBM,GAAevc,CAAS,CAAC,EAC/DyN,EAAK,YAAYgO,EAAYQ,EAAMjc,CAAS,CAAC,CAC/C,CAAC,EACD+c,EAAO,OAAO,OAAQrb,EAAK,YAAa,EAAE,CAAC,EAC3C+L,EAAK,YAAYsP,CAAM,CACzB,KAAO,CACL,IAAM1M,EAAM7I,EAAO,YAAYzH,EAAE,MAAO,WAAW,CAAC,EAC9CwM,EAAUoQ,EAAW,OAASA,EAAaxT,EACjDkH,EAAI,YAAYtQ,EAAE,OAAQ,UAAWwM,EAAQ,IAAK,GAAc,IAAMvM,CAAS,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,EAC3F,IAAMgd,EAAS7T,EAAQ,OAASoD,EAAQ,QAEpCyQ,GAAU7T,EAAQ,OAAS,KAC7B4T,EAAO,OAAOC,EAAS,IAAIA,IAAW,MAAOtb,EAAK,cAAe,EAAE,CAAC,EACpE2O,EAAI,YAAY0M,CAAM,EAE1B,CACF,CAIA,SAASE,GAAgB,CAAE,MAAqB,CAC9C,IAAMtU,EAAYF,GAAYtL,CAAE,EAChC,GAAI,CAACwL,EAAW,OAChB,IAAMgL,EAAUwI,EACdxT,EAAU,OAAS,mBAAqB,YACxCA,EAAU,OAAS,wBAA0B,eAC7C,WACF,EAGMhH,EAAO5B,EAAE,MAAO4I,EAAU,OAAS,iBAAmB,MAAM,EAClEhH,EAAK,YAAY5B,EAAE,OAAQ,OAAQ4I,EAAU,IAAI,CAAC,EAClD,IAAMuU,EAAOvb,EAAK,YAAY5B,EAAE,OAAQ,WAAW,CAAC,EACpDmd,EAAK,OAAOxb,EAAK,MAAM,EAAGA,EAAK,OAAO,CAAC,EACvCwb,EAAK,kBAAmB,UAAU,IAAI,WAAW,EACjDA,EAAK,iBAAkB,UAAU,IAAI,aAAa,EAClDvJ,EAAQ,YAAY8H,EAAY9Z,EAAMgH,EAAU,IAAI,CAAC,EAIrD,IAAMjK,EAAQkK,GAAeD,CAAS,EACtC,GAAIjK,EAAM,OAAQ,CAChBiV,EAAQ,YAAY5T,EAAE,MAAO,OAAQ,sBAAsB,CAAC,EAC5D,IAAM0N,EAAOkG,EAAQ,YAAY5T,EAAE,MAAO,OAAO,CAAC,EAClD,IAAK,IAAMpB,KAAQD,EACjB+O,EAAK,YAAY1N,EAAE,OAAQ,YAAapB,EAAK,IAAI,CAAC,EAClD8O,EAAK,YAAYgO,EAAY1b,EAAE,OAAQ,QAASpB,EAAK,KAAK,EAAGA,EAAK,KAAK,CAAC,CAE5E,CAEA,GAAIgK,EAAU,WAAY,CAExB,IAAMwU,EAAUxU,EAAU,WAAW,MAAM,GAAG,EACxC+F,EAAOyO,EAAQ,IAAI,GAAK,GACxBC,EAASzJ,EAAQ,YAAY5T,EAAE,MAAO,QAAQ,CAAC,EACrDqd,EAAO,YAAYrd,EAAE,OAAQ,QAAS,QAAQ,CAAC,EAC/C,IAAMsd,EAAOD,EAAO,YAAYrd,EAAE,OAAQ,MAAM,CAAC,EACjDod,EAAQ,QAAS,GAAWE,EAAK,OAAOC,EAAS,IAAK,SAAS,cAAc,KAAK,CAAC,CAAC,EACpFD,EAAK,YAAYtd,EAAE,OAAQ,OAAQ2O,CAAI,CAAC,EACxC+M,EAAY4B,EAAM1U,EAAU,UAAU,CACxC,CAEA,IAAM/I,EAAO+I,EAAU,QACvB,GAAIA,EAAU,QAAU/I,EAAM,CAC5B,IAAMyQ,EAAMsD,EAAQ,YAAY5T,EAAE,MAAO,YAAY,CAAC,EAChDwd,EAASlN,EAAI,YAAYtQ,EAAE,SAAU,cAAc,CAAC,EAC1Dwd,EAAO,OAAO7b,EAAK,YAAa,EAAE,EAAG,kBAAkB,EACvD6b,EAAO,iBAAiB,YAAergB,EAAI0C,CAAI,CAAC,EAChDyQ,EAAI,YAAYtQ,EAAE,OAAQ,OAAQ,WAAW5C,EAAG,UAAU,WAAW,CAAC,CACxE,CACF,CAIA,SAASqgB,IAAsB,CAC7B,EAAQ,YAAc,KACtBngB,EAAS,CACX,CAGA,SAASogB,GAAY,EAAwB,EAAgC,CAC3E,IAAMra,EAAOrD,EAAE,OAAQ,QAAQC,GAAW,EAO1C,OANKqa,GAAM,OAGLA,EAAK,OAAOjX,EAAK,YAAY4Y,GAAO3B,EAAK,MAAO,EAAI,CAAC,EACzDjX,EAAK,OAAO2Y,GAAU1B,CAAI,CAAC,GAH3B,EAAK,YAAc,OAKdjX,CACT,CAGA,SAASsa,GAAc,EAAwB,CAC7C,IAAMC,EAAW5R,EAAQ,YACzB,GAAI,CAAC4R,GAAU,YAAa,OAC5B,IAAMhK,EAAUwI,EAAM,SAAS,EACzByB,EAAOjK,EAAQ,kBAAmB,YAAY5T,EAAE,SAAU,cAAc,CAAC,EAC/E6d,EAAK,OAAOlc,EAAK,QAAS,EAAE,EAAG,OAAQ3B,EAAE,MAAO,GAAI,KAAK,CAAC,EAC1D6d,EAAK,iBAAiB,QAASJ,EAAa,EAE5C,IAAMK,GAAO,EAAgB,EAAa,IAAiB,CACzD,IAAMxN,EAAMsD,EAAQ,YAAY5T,EAAE,MAAO,KAAK,CAAC,EAC/CsQ,EAAI,YAAYtQ,EAAE,OAAQ+d,IAAW,IAAM,SAAW,cAAeA,CAAM,CAAC,EAC5EzN,EAAI,YAAYtQ,EAAE,OAAQ,OAAQ8I,EAAY1L,CAAE,CAAC,CAAC,EAClDkT,EAAI,YAAYtQ,EAAE,OAAQ,QAAS2G,EAAcvJ,EAAIA,EAAG,sBAAsB,CAAC,CAAC,EAAE,OAAS,EAAE,CAAC,EAC9FkT,EAAI,YAAYtQ,EAAE,OAAQ,QAAQ,CAAC,EACnCsQ,EAAI,YAAYtQ,EAAE,OAAQ,OAAQge,CAAI,CAAC,CACzC,EAEA,GADAF,EAAI,IAAKF,EAAU,UAAU,EACzBA,IAAaK,EAAQ,GAAI,CAC3BrK,EAAQ,YAAY5T,EAAE,MAAO,kBAAmB,2CAA2C,CAAC,EAC5F,MACF,CACA8d,EAAI,IAAKG,EAAQ,GAAI,QAAQ,EAG7B,GAAM,CAAE,cAAa,aAAc9D,GADVD,GAAa0D,EAAU,iBAAiBA,CAAQ,EAAGK,EAAQ,QACpC,EAAkBA,EAAQ,QAAQ,EAC5E7N,EAAQsK,EAAY,SAAW,EAAI,eAAiB,GAAGA,EAAY,OAAO,cAChF9G,EAAQ,YAAY5T,EAAE,MAAO,kBAAmB,GAAGoQ,EAAM,KAAK8N,EAAU,WAAW,CAAC,EACpF,IAAK,IAAMC,KAAczD,EAAa,CACpC,IAAMpK,EAAMsD,EAAQ,YAAY5T,EAAE,MAAO,aAAa,CAAC,EACvDsQ,EAAI,YAAYtQ,EAAE,OAAQ,QAASme,EAAW,IAAI,CAAC,EACnD7N,EAAI,OACFoN,GAAYS,EAAW,OAAQ,WAAW,EAC1Cxc,EAAK,OAAO,EACZ+b,GAAYS,EAAW,MAAO,UAAU,CAC1C,CACF,CACF,CAIA,SAASC,GAAQ,EAAa,EAA8B,CAC1D,IAAM9N,EAAMtQ,EAAE,MAAO,UAAU,EAC/BsQ,EAAI,YAAYtQ,EAAE,OAAQ,QAAS,MAAM,CAAC,EAC1C,IAAMwB,EAAOuH,EAAS3L,CAAE,EACxBse,EAAYpL,EAAI,YAAYtQ,EAAE,OAAQ,QAASwB,CAAI,CAAC,EAAGA,CAAI,EAC3D8O,EAAI,YAAYtQ,EAAE,OAAQ,QAAQ,CAAC,EAGnC,GAAM,CAACgJ,EAAOC,GAAUoV,EAAQ,MAAM,KAAK,EACrC3Q,EAAO4C,EAAI,YAAYtQ,EAAE,OAAQ,SAAS,CAAC,EAC3C8B,GAAU,EAAc,IAAsB,CAElD4L,EADkB,YAAY1N,EAAE,OAAQ,UAAUC,GAAW,CAC7D,CAAA,CAAK,OAAO4B,GAAYyc,CAAI,EAAGA,CAAI,CACrC,EAIA,OAHAxc,EAAOkH,EAAO,UAAU,EACxB0E,EAAK,YAAY1N,EAAE,OAAQ,GAAI,GAAG,CAAC,EACnC8B,EAAOmH,EAAQ,WAAW,EACnBqH,CACT,CAGA,SAASiO,GAAW,EAAkB,EAAiE,CACrG,OAAOC,EAAM,SAAS,CAAC5a,EAAO0W,KAC5BM,GAAeqD,EAAQ,GAAI3D,GAAM,OAAS,CAAC,EAAG2D,EAAQ,QAAQ,CAAC,CAAC,IAAK,GAAU,CAC7E,IAAM3N,EAAMtQ,EAAE,MAAO,MAAM,EAC3BsQ,EAAI,YAAYtQ,EAAE,OAAQ,QAAS4D,CAAK,CAAC,EACzC,IAAM0Z,EAAOrC,GAAUE,EAAO3d,EAAO,aAAa,EAElD,OADAke,EAAYpL,EAAI,YAAYtQ,EAAE,OAAQ,aAAcsd,GAAQnC,CAAK,CAAC,EAAGmC,GAAQnC,CAAK,EAC3E7K,CACT,CAAC,CACH,CACF,CAEA,SAASmO,GAAa,EAAwB,CAC5C,GAAM,CAAE,KAAI,MAAOR,EACb1E,EAAU5S,EAAcvJ,EAAIA,EAAG,sBAAsB,CAAC,EACtDwW,EAAUwI,EAAM,SAAU7C,EAAU,gBAAkB,EAAE,EAC9D3F,EAAQ,YAAYwK,GAAQhhB,EAAIkf,EAAS2B,EAAS,SAAU,MAAM,CAAC,EAAE,MAAQ,WAAW,CAAC,EAEzF,IAAM3D,GAAQ,EAAqB,IAAiBgC,EAAS2B,EAASS,EAAa9c,CAAI,EACjF4X,EAASlW,EAAG,QAAQ,SAAS,MAAM,EACnCqb,EAAmE,CACvE,CAAC,IAAKpF,EAAU,OAAOA,EAAQ,CAAC,EAAI,EAAE,EACtC,CAAC,IAAKA,EAAU,OAAOA,EAAQ,CAAC,EAAI,EAAE,EACtC,CAAC,UAAWjW,EAAG,OAAO,EACtB,CAAC,YAAakW,EAASlW,EAAG,eAAiBA,EAAG,WAAa,SAAW,GAAK,SAAW,EAAE,EACxF,CAAC,QAASgX,EAAK,SAAU,OAAO,CAAC,EAAE,KAAK,EACxC,CAAC,UAAWA,EAAK,SAAU,SAAS,CAAC,EAAE,KAAK,EAC5C,CAAC,MAAOA,EAAK,SAAU,KAAK,CAAC,EAAE,MAAOA,EAAK,SAAU,KAAK,CAAC,EAC3D,CAAC,SAAUA,EAAK,QAAS,QAAQ,CAAC,EAAE,MAAOA,EAAK,QAAS,QAAQ,CAAC,EAClE,CAAC,SAAUA,EAAK,QAAS,QAAQ,CAAC,EAAE,MAAOA,EAAK,QAAS,QAAQ,CAAC,EAClE,CAAC,UAAWA,EAAK,QAAS,SAAS,CAAC,EAAE,MAAOA,EAAK,QAAS,SAAS,CAAC,EACrE,CAAC,SAAUA,EAAK,OAAQ,cAAc,CAAC,EAAE,MAAOA,EAAK,OAAQ,cAAc,CAAC,CAC9E,EACM7Y,EAAOmS,EAAQ,YAAY5T,EAAE,MAAO,OAAO,CAAC,EAClD2e,EAAM,SAAS,CAAC/a,EAAO3F,KAAWA,GAASwD,EAAK,YAAYiL,GAAK9I,EAAO3F,CAAK,CAAC,CAAC,EAG/E,IAAMoe,EAAkD,CACtD,CAAC,WAAY/B,EAAK,SAAU,UAAU,CAAC,EACvC,CAAC,UAAWA,EAAK,SAAU,SAAS,CAAC,EACrC,CAAC,SAAUA,EAAK,QAAS,QAAQ,CAAC,EAClC,CAAC,WAAYA,EAAK,QAAS,UAAU,CAAC,CACxC,EACA+B,EAAK,SAAS,CAACzY,EAAO0M,KAASA,GAAOsD,EAAQ,YAAYlH,GAAK9I,EAAO0M,EAAI,MAAO,EAAI,CAAC,CAAC,EAEvF,IAAMsO,EAAY,CAChB,GAAGD,EAAM,KAAK,CAAC/a,GAAS0M,KAAqC,CAAC1M,EAAO0M,CAAG,CAAC,EACzE,GAAG+L,CACL,EACAzI,EAAQ,OAAO,GAAG2K,GAAWN,EAASW,CAAS,CAAC,CAClD,CAKA,SAASC,GAAS,EAAkB,EAAoB,CACtD,IAAK,IAAMxb,KAAQJ,EAAO,CACxB,IAAMhF,EAAQ,GAAG8d,GAAUvX,EAAMnB,EAAK,EAAE,IACxCjD,EAAI,YAAYsb,EAAY1b,EAAE,OAAQ,eAAeqD,IAAQ0P,GAAM9U,CAAK,CAAC,EAAG6d,GAAO7d,CAAK,CAAC,CAAC,CAC5F,CACF,CAGA,SAAS6gB,GAAU,CAAE,KAAI,MAAqB,CAC5C,IAAMra,EAASvB,EAAQI,EAAI,QAAQ,EAC7Byb,EAAU7b,EAAQI,EAAI,SAAS,EAC/B0b,EAAS9b,EAAQI,EAAI,QAAQ,EAC7B9B,EAAON,GAAW9D,EAAIA,EAAG,sBAAsB,CAAC,EAChD6hB,EAAezd,EAAK,MAAQud,EAAQ,KAAOA,EAAQ,MAAQC,EAAO,KAAOA,EAAO,MAChFE,EAAgB1d,EAAK,OAASud,EAAQ,IAAMA,EAAQ,OAASC,EAAO,IAAMA,EAAO,OACjFG,GAAW,EAA4B,IAC3CpG,GACEnX,EACAqB,EAAM,IAAK,GAAS8Y,GAAUvX,EAAMnB,EAAK,CAAC,EAC1C2I,EAAQ,KACR4M,GAAa,EACbpb,EAAO,UACT,EAGI4hB,EADUhD,EAAM,KACJ,CAAA,CAAQ,YAAYpc,EAAE,MAAO,YAAY,CAAC,EACtDqf,EAAa3D,EAAY1b,EAAE,OAAQ,WAAY,QAAQ,MAASmf,EAAQ,SAAU1a,CAAM,CAAC,EAC/F,EAAW,MAAQ,+BACnB2a,EAAU,YAAYC,CAAU,EAChCR,GAASO,EAAW3a,CAAM,EAE1B,IAAM6a,EAAaF,EAAU,YAAYpf,EAAE,MAAO,aAAa,CAAC,EAC1Duf,EAAc7D,EAAY1b,EAAE,OAAQ,WAAY,SAAS,MAC7Dmf,EAAQ,UAAWJ,CAAO,CAC5B,EACA,EAAY,MAAQ,+BACpBO,EAAW,YAAYC,CAAW,EAClCV,GAASS,EAAYP,CAAO,EAE5B,IAAMzf,EAAU,GAAGyc,GAAUkD,CAAY,EAAE,KAAKlD,GAAUmD,CAAa,IACvEI,EAAW,YAAYtf,EAAE,MAAO,cAAeV,CAAO,CAAC,CACzD,CAKA,SAASkgB,GAAU,EAA0B,CAC3C,IAAM5d,EAAO,sBAAsB,KAAKpD,CAAQ,CAAC,GAAG,IAAMA,EAC1D,OAAOoD,IAAS,OAAS,UAAYA,CACvC,CAOA,SAAS6d,GAAS,EAAY,EAAc,EAA+B,CACzE,GAAM,CAAE,KAAI,KAAI,YAAaxB,EACvB3N,EAAMtQ,EAAE,MAAO,WAAW,EAC1B0f,GAAiC,EAAS,IAAiBhE,EAAY9Y,EAAM1C,EAAMoQ,CAAG,EACtF,CAACyB,EAAKE,EAAQ,QAAUH,GAAYwI,EAAK,KAAK,CAAC,CAAC,MAAM,GAAG,EAEzDhM,EAAOgC,EAAI,YAAYtQ,EAAE,MAAO,YAAY,CAAC,EACnDsO,EAAK,YAAY2N,GAAO3B,EAAK,KAAK,CAAC,EACnChM,EAAK,YAAYoR,EAAO1f,EAAE,OAAQ,QAAS+R,CAAG,EAAGA,CAAG,CAAC,EACjDG,GAAaH,CAAG,GAAGzD,EAAK,YAAYtO,EAAE,OAAQ,QAASiS,CAAK,CAAC,EAEjE,IAAMqG,EAAWsC,GAAexd,EAAIkd,EAAK,OAAS,CAAC,EAAGZ,CAAQ,EACxDiG,EAAUrH,EAAS,QAAU,CAACkD,GAAUlY,CAAE,EAAI,CAAC,EAAIqO,GAAgBrO,EAAIgX,EAAK,KAAK,EACjFsF,EAAU,CAACtH,EAAS,QAAU,CAACqH,EAAQ,QAAUnE,GAAUlY,CAAE,EAC/Dqc,EAAQ,QAAQrR,EAAK,YAAYtO,EAAE,OAAQ,QAAS,eAAe,CAAC,EACpE4f,IAEFtR,EADmB,YAAYtO,EAAE,OAAQ,eAAe,CACxD,CAAA,CAAM,OAAO2B,EAAK,UAAW,EAAE,EAAG,UAAU,EAC5C2O,EAAI,UAAU,IAAI,YAAY,GAGhChC,EAAK,YAAYtO,EAAE,OAAQ,QAAQ,CAAC,EACpC,IAAM6f,EAAOvR,EAAK,YAAYtO,EAAE,OAAQ,MAAM,CAAC,EAC/C6f,EAAK,YAAY7f,EAAE,OAAQ,OAAQge,CAAI,CAAC,EACxC6B,EAAK,YAAY7f,EAAE,OAAQ,WAAW,CAAC,CAAC,CAAC,OAAO2B,EAAK,OAAQ,EAAE,EAAG,MAAM,EACxEke,EAAK,YAAY7f,EAAE,OAAQ,QAAQ,CAAC,CAAC,CAAC,OAAO2B,EAAK,QAAS,EAAE,EAAG,QAAQ,EAExE,IAAK,IAAMwZ,KAAS7C,EAAU,CAC5B,IAAMgF,EAAOrC,GAAUE,EAAO3d,EAAO,aAAa,EAC9C8f,GAAMhN,EAAI,YAAYoP,EAAO1f,EAAE,MAAO,aAAcsd,CAAI,EAAGA,CAAI,CAAC,EACpEhN,EAAI,YAAYoP,EAAO1f,EAAE,MAAOsd,EAAO,uBAAyB,UAAWnC,CAAK,EAAGA,CAAK,CAAC,EACzF,IAAMG,EAAQD,GAAWF,CAAK,EAC1BG,EAAM,QAAQhL,EAAI,YAAYwP,GAAUxE,EAAOhY,EAAG,iBAAiB6X,CAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CACvF,CAMA,OAJAwE,EAAQ,MAAM,EAAG,CAAC,CAAC,CAAC,QAAS,GAAU,CACrCrP,EAAI,YAAYoP,EAAO1f,EAAE,MAAO,uBAAwBmb,CAAK,EAAGA,CAAK,CAAC,CACxE,CAAC,EACGyE,GAAStP,EAAI,YAAYtQ,EAAE,MAAO,YAAa,6CAA6C,CAAC,EAC1FsQ,CACT,CAGA,SAASwP,GAAU,EAAyC,EAA8B,CACxF,IAAMxR,EAAOtO,EAAE,MAAO,OAAO,EAC7B,IAAK,GAAM,CAAC+f,EAAO9hB,KAAUqd,EAAO,CAClC,IAAM0E,EAAO1R,EAAK,YAAYtO,EAAE,OAAQ/B,IAAU6Q,EAAU,iBAAmB,MAAM,CAAC,EAClFoD,GAAajU,CAAK,GAAG+hB,EAAK,YAAY/D,GAAOhe,EAAO,EAAI,CAAC,EAC7D+hB,EAAK,OAAO/hB,EAAO+B,EAAE,OAAQ,QAASwf,GAAUO,CAAK,CAAC,CAAC,EACvDrE,EAAYsE,EAAM/hB,CAAK,CACzB,CACA,OAAOqQ,CACT,CAEA,IAAM2R,GAA+D,CACnE,CAAC,QAAS,aAAc,YAAY,EACpC,CAAC,QAAS,eAAgB,QAAQ,EAClC,CAAC,OAAQ,QAAS,MAAM,EACxB,CAAC,OAAQ,OAAQ,MAAM,EACvB,CAAC,OAAQ,SAAU,QAAQ,CAC7B,EAEA,SAASC,GAAa,EAAwB,CAC5C,IAAM/L,EAAO8L,GAAY,SAAS,CAACrM,EAAShS,EAAMoc,KAAU,CAC1D,IAAM1D,EAAOgC,EAAS2B,EAASrK,EAAShS,CAAI,EAC5C,OAAO0Y,EAAO,CAACmF,GAASnF,EAAM0D,EAAMC,CAAO,CAAC,EAAI,CAAC,CACnD,CAAC,EACGjS,EAAQ,aACVmI,EAAK,KAAKsL,GAAS,CAAE,KAAM,SAAU,MAAOzT,EAAQ,YAAa,MAAO,EAAK,EAAG,SAAUiS,CAAO,CAAC,EAE/F9J,EAAK,QACViI,EAAM,QAAQ,CAAC,CAAC,YAAYpc,EAAE,MAAO,YAAY,CAAC,CAAC,CAAC,OAAO,GAAGmU,CAAI,CACpE,CAIA,SAASgM,GAAiB,EAAwB,CAChD,GAAM,CAAE,MAAOlC,EACTmC,EAAO9D,EAAS2B,EAAS,OAAQ,MAAM,EAC7C,GAAI,CAACmC,EAAM,OACX,IAAMxM,EAAUwI,EAAM,YAAY,EAC5BiE,EAAS/c,EAAG,WAAW,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,WAAW,IAAK,EAAE,EAC7DsQ,EAAQ,YAAYlH,GAAK,SAAU2T,CAAM,CAAC,EAE1C,IAAMngB,EAAQ,GAAiBoc,EAAS2B,EAAS,OAAQrc,CAAI,CAAC,EAAE,MAC1D+c,EAAsD,CAC1D,CAAC,SAAUrb,EAAG,UAAU,EACxB,CAAC,OAAQA,EAAG,QAAQ,EACpB,CAAC,OAAQA,EAAG,UAAU,EACtB,CAAC,WAAYpD,EAAK,SAAS,CAAC,EAC5B,CAAC,YAAaA,EAAK,WAAW,CAAC,EAC/B,CAAC,QAASA,EAAK,OAAO,CAAC,EACvB,CAAC,aAAcA,EAAK,YAAY,CAAC,CACnC,EACMuB,EAAOmS,EAAQ,YAAY5T,EAAE,MAAO,OAAO,CAAC,EAClD2e,EAAM,SAAS,CAAC/a,EAAO3F,KAAWA,GAASwD,EAAK,YAAYiL,GAAK9I,EAAO3F,CAAK,CAAC,CAAC,EAC/E2V,EAAQ,OAAO,GAAG2K,GAAWN,EAAS,CAAC,CAAC,OAAQmC,CAAI,CAAC,CAAC,CAAC,CACzD,CAIA,SAASE,GAAa,CAAE,MAAqB,CAC3C,IAAMC,EAAQ1I,GAAYvU,CAAE,EAC5B,GAAI,CAACid,EAAM,OAAQ,OACnB,IAAM3M,EAAUwI,EAAM,QAAQ,EAC9B,IAAK,IAAMzO,KAAQ4S,EAAO,CACxB,IAAMjQ,EAAMsD,EAAQ,YAAY5T,EAAE,MAAO,YAAY,CAAC,EACtDsQ,EAAI,YAAYvO,GAAY4L,EAAK,KAAK,CAAC,EACvC2C,EAAI,YAAYtQ,EAAE,OAAQ,WAAY2N,EAAK,QAAQ,CAAC,EACpD2C,EAAI,YAAYtQ,EAAE,OAAQ,WAAY2N,EAAK,QAAQ,CAAC,EACpD2C,EAAI,YAAYtQ,EAAE,OAAQ,SAAU2N,EAAK,MAAM,CAAC,EAChD,EAAI,MAAQ,gBACZ+N,EAAYpL,EAAK,GAAG3C,EAAK,SAAS,GAAGA,EAAK,SAAS,GAAGA,EAAK,QAAQ,CACrE,CACF,CAKA,SAAS6S,GAAU,EAAoB,EAAuB,EAAiC,CAC7F,IAAMC,EAAW,MAAM,KAAKjM,EAAM,iBAA8B,UAAU,CAAC,EACrEkM,MAAa,CACjBD,EAAS,QAAS,GAAY,CAC5BE,EAAQ,aAAa,eAAgB,OAAOA,EAAQ,QAAQ,QAAU7R,EAAQ,CAAC,CAAC,CAClF,CAAC,EACD0F,EAAM,UAAU,OAAO,QAASA,EAAM,cAAc,6BAA6B,IAAM,IAAI,CAC7F,EACAiM,EAAS,QAAS,GAAY,CAC5BE,EAAQ,iBAAiB,YAAe,CACtC3P,EAAO2P,EAAQ,QAAQ,OAAS,EAAE,EAClCD,EAAK,EACLpjB,EAAS,CACX,CAAC,CACH,CAAC,EACDojB,EAAK,CACP,CAEA,SAASE,GAAe,EAA0B,CAChDJ,GACEtR,EAAKC,EAAO,sBAAsB,MAC5BnD,EAAQ,KACb,GAAU,CACT,EAAQ,KAAO/N,IAAU,MAAQ,MAAQ,KACzC8X,GAAW5G,CAAK,CAClB,CACF,EACAqR,GACEtR,EAAKC,EAAO,sBAAsB,MAC5B,OAAOnD,EAAQ,QAAQ,EAC5B,GAAU,CACT,EAAQ,SAAW,OAAO/N,CAAK,CACjC,CACF,EAEA,IAAM4iB,EAAU3R,EAAKC,EAAO,yBAAyB,EAC/C2R,MAAoBD,EAAQ,aAAa,eAAgB,OAAO7U,EAAQ,cAAgB,IAAI,CAAC,EACnG6U,EAAQ,iBAAiB,YAAe,CACtC,EAAQ,YAAc7U,EAAQ,YAAc,KAAOlP,EAAc,EACjEQ,EAAS,CACX,CAAC,EAED,SAAS,iBAAiB,UAAY,GAAM,CACtC,EAAE,MAAQ,UAAY0O,EAAQ,aAAayR,GAAc,CAC/D,CAAC,EAED,IAAMsD,EAAO7R,EAAKC,EAAO,sBAAsB,EAC/C,EAAK,OAAS,CAAC,OAAO,WACtB4R,EAAK,iBAAiB,QAAS,SAAY,CACzCA,EAAK,aAAa,eAAgB,MAAM,EACxC,GAAI,CACF,EAAQ,aAAe,MAAM,IAAI,OAAO,WAAY,CAAC,CAAC,KAAK,EAAA,CAAG,OAChE,MAAQ,CAER,CACAA,EAAK,aAAa,eAAgB,OAAO,EACzCzjB,EAAS,CACX,CAAC,EAEDoe,EAAYxM,EAAKC,EAAO,sBAAsB,MAAS,CACrD,IAAMvS,EAASE,EAAc,EAC7B,OAAOF,EAAS+O,GAAY/O,CAAM,EAAI,SAAS,IACjD,CAAC,EAED,GAAqBkkB,CACvB,CAGA,IAAIE,OAAiC,CAAC,EAItC,SAASC,GAAY,EAAmB,CACtC,IAAM3d,EAAK,iBAAiBlG,CAAE,EACxBsc,EAAW5C,GAAkB,EAC7BmH,EAAmB,CAAE,KAAI,KAAI,WAAU,SAAU/D,GAAa9c,EAAIkG,EAAIoW,CAAQ,CAAE,EAEtFsH,GAAmB,EACnBrE,GAAavf,CAAE,EACf,IAAM4W,EAAYqC,EAAK,UACvBA,EAAK,gBAAgB,EACrBsH,GAAcM,CAAO,EACrBf,GAAgBe,CAAO,EACvBQ,GAAaR,CAAO,EACpBa,GAAUb,CAAO,EACjBiC,GAAajC,CAAO,EACpBkC,GAAiBlC,CAAO,EACxBqC,GAAarC,CAAO,EACpB,EAAK,UAAYjK,CACnB,CCllBA,IAAMkN,GAAc,CAAC,CDqlBnB,MAAM,EAAO,CACX,EAAShS,EAAKC,EAAO,iBAAiB,EACtC,EAAOC,EAAQD,EAAO,OAAO,EAC7ByR,GAAezR,CAAK,CACtB,EACA,OAAO,EAAI,EAAQ,CACbjS,IAAW,WAAU,EAAK,UAAY,GAC1C+jB,GAAY7jB,CAAE,CAChB,CC7lBmB,EAAU0X,GAAQY,GAAWpE,GAAY8E,EAAY,EACpE+K,GAAY,IACZC,GAAY,IAEdjS,EAA4B,KAGhC,SAASkS,IAAmB,CAE1Bxe,GAAgB,aAAc,GADdsM,IAAU,MAAQA,EAAM,MAAM,UAAY,OACfA,EAAO,YAAc,EAAE,GAAG,CACvE,CAEA,SAASmS,GAAa,EAAiB,EAA0B,CAC/DlkB,EAAG,UAAU,OAAO,eAAgBmkB,CAAS,EAC7CF,GAAW,CACb,CAGA,SAASG,GAAiB,EAAuB,CAC/CtS,EAAK9R,EAAI,SAAS,CAAC,CAAC,iBAAiB,YAAc,GAAS,CAC1DqkB,EAAK,eAAe,EACpB,IAAMC,EAAatkB,EAAG,YAChBukB,EAAQ,GAAkB,CAC9B,IAAM3Y,EAAQ0Y,EAAaD,EAAK,QAAU9W,EAAE,QAC5C,EAAG,MAAM,MAAQ,GAAG,KAAK,IAAIwW,GAAW,KAAK,IAAIC,GAAWpY,CAAK,CAAC,EAAE,IACpEqY,GAAW,CACb,EACMxD,MAAa,CACjB,SAAS,oBAAoB,YAAa8D,CAAI,EAC9C,SAAS,oBAAoB,UAAW9D,CAAI,EAC5C9H,GAAW3Y,CAAE,CACf,EACA,SAAS,iBAAiB,YAAaukB,CAAI,EAC3C,SAAS,iBAAiB,UAAW9D,CAAI,CAC3C,CAAC,CACH,CAGA,SAAS+D,GAAc,EAAuB,CAC5C,IAAM5R,EAASd,EAAK9R,EAAI,cAAc,EAChCgQ,EAAO8B,EAAK9R,EAAI,YAAY,EAC5BmY,EAAW,GAAkB,CACjC,EAAK,OAAS,CAACC,EACfxF,EAAO,aAAa,gBAAiB,OAAOwF,CAAI,CAAC,CACnD,EACAxF,EAAO,iBAAiB,QAAU,GAAM,CACtC,EAAE,gBAAgB,EAClBuF,EAAQnI,EAAK,MAAM,CACrB,CAAC,EAEDhQ,EAAG,iBAAiB,YAAemY,EAAQ,EAAK,CAAC,EACjDnY,EAAG,iBAAiB,UAAY,GAAM,CAChC,EAAE,MAAQ,UAAUmY,EAAQ,EAAK,CACvC,CAAC,EACDrG,EAAK9B,EAAM,wBAAwB,CAAC,CAAC,iBAAiB,YAAe4I,GAAO5Y,EAAI,WAAW,CAAC,EAC5F8R,EAAK9B,EAAM,WAAW,CAAC,CAAC,iBAAiB,YAAekU,GAAalkB,EAAI,EAAI,CAAC,CAChF,CAEA,SAASykB,IAA2B,CAClC,IAAMzkB,EAAKuF,EAAM3C,EAAE,MAAO,OAAO,CAAC,EAgBlC,MAfA,GAAG,UAAYrD,GACfkZ,GAAWzY,CAAE,EAGbA,EAAG,iBAAiB,UAAY,GAAM,EAAE,gBAAgB,CAAC,EACzD8R,EAAK9R,EAAI,OAAO,CAAC,CAAC,iBAAiB,QAAU,GAAM,CACjD,IAAMiS,EAAO1E,EAAE,OAAmB,QAAqB,MAAM,CAAC,EAAE,QAAQ,IACpE0E,GAAK2G,GAAO5Y,EAAIiS,CAAG,CACzB,CAAC,EACDH,EAAK9R,EAAI,QAAQ,CAAC,CAAC,iBAAiB,QAASC,CAAK,EAClDukB,GAAcxkB,CAAE,EAChB8R,EAAK9R,EAAI,OAAO,CAAC,CAAC,iBAAiB,YAAekkB,GAAalkB,EAAI,EAAK,CAAC,EACzEokB,GAAiBpkB,CAAE,EAEnB8jB,GAAK,QAAS,GAAQ7R,EAAI,MAAMjS,CAAE,CAAC,EAC5BA,CACT,CAEA,SAAgB0kB,IAAmB,CACjC/kB,GAAa,EAAI,IAAW,CACrBK,GAKL,IAAUykB,GAAY,EACtB,EAAM,MAAM,QAAU,GACtB,EAAK1S,EAAO,aAAa,CAAC,CAAC,YAAc,aAAarG,EAAY1L,CAAE,IAChEF,IAAW,UAAUokB,GAAanS,EAAO,EAAK,EAClD+R,GAAK,QAAS,GAAQ7R,EAAI,OAAOjS,EAAIF,CAAM,CAAC,EAC5CmkB,GAAW,IATLlS,IAAO,EAAM,MAAM,QAAU,QACjCkS,GAAW,EASf,CAAC,CACH,CCxEA,SAASre,IAAc,CACrBpF,EAAuB,EACvBsP,GAAc,EACd4U,GAAW,EACXtX,GAAW,EACXoD,GAAe,EACf7C,GAAc,EAIdhL,EAAgB,EAChB,SAAS,iBAAiB,uBAA0BA,EAAgB,CAAC,EACrE,OAAO,iBAAiB,WAAcA,EAAgB,CAAC,EACvD8L,GAAY,EAEZ,OAAO,MAAQ,CAAE,MAAK,gBAAe,kBAAiB,aAAW,WAAU,CAC7E,CAGK,OAAO,OAAO7I,GAAM,C"}
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "gromajs",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.3.0",
4
+ "description": "Groma: survey any design before you build it. A Figma-style inspector for design handoff pages: box model, specs, design tokens, components and distances on any HTML page.",
5
+ "keywords": [
6
+ "groma",
7
+ "inspector",
8
+ "design-handoff",
9
+ "design-tokens",
10
+ "devtools",
11
+ "figma",
12
+ "dev-mode",
13
+ "ai-design"
14
+ ],
15
+ "author": "Gabriel Franco (https://github.com/franco-dias)",
16
+ "license": "MIT",
17
+ "type": "module",
18
+ "main": "./dist/groma.js",
19
+ "unpkg": "./dist/groma.js",
20
+ "jsdelivr": "./dist/groma.js",
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "serve": "vite",
26
+ "inspect": "vite build --logLevel warn && node scripts/inspect.mjs",
27
+ "build": "tsc --noEmit && vite build",
28
+ "typecheck": "tsc --noEmit",
29
+ "test": "vitest run",
30
+ "prepublishOnly": "npm run test && npm run build",
31
+ "test:unit": "vitest run --project unit",
32
+ "test:browser": "vitest run --project browser",
33
+ "release:check": "npm publish --dry-run"
34
+ },
35
+ "devDependencies": {
36
+ "@vitest/browser-playwright": "^5.1.0",
37
+ "playwright": "^1.65.0",
38
+ "typescript": "^5.9.3",
39
+ "vite": "^8.3.7",
40
+ "vitest": "^5.1.0"
41
+ },
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/franco-dias/groma.git"
45
+ },
46
+ "homepage": "https://github.com/franco-dias/groma#readme",
47
+ "bugs": {
48
+ "url": "https://github.com/franco-dias/groma/issues"
49
+ }
50
+ }