@hazem3500/komo 0.9.27 → 0.9.29

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/README.md CHANGED
@@ -80,11 +80,14 @@ Comments are shared across deployments by default. Use `pnpm exec komo init --br
80
80
  - Group tags: add them while writing a comment or on an open thread, with suggestions from tags already in the project.
81
81
  - Safe HTTP(S) links and simple Markdown links in comment messages.
82
82
  - Searchable sidebar, draggable dock, and Google or guest profiles.
83
+
83
84
  - Dismiss the toolbar for the current tab with ×; restore it with Shift+K or `controller.open()`. Set `showToolbar: false` to start hidden.
84
85
  - Keyboard shortcuts: C to comment, V to browse, D to toggle Design mode, L to view all comments, Shift+K to restore. Remap or disable them with `shortcuts`; see [Choosing shortcuts](#choosing-shortcuts).
85
86
  - Copy open feedback with selectors, source paths, page URLs, replies, and geometry into an agent.
86
87
  - Hosted Cloudflare storage, or your own Worker/D1 or Node/PostgreSQL service.
87
88
 
89
+ Comment pins stay anchored during ordinary pointer drags. Authors and verified reviewers can choose **Comment actions → Move pin** to reposition one explicitly: drag it, or use arrow keys (Shift for larger steps), then Enter or **Save position**. Escape or **Cancel move** restores its previous position without saving. Completed moves use the existing thread anchor endpoint and remain attached after reload; the page element itself never moves. Dragging a floating comment card is independent of pin placement.
90
+
88
91
  Design mode edits the rendered page only. Preview styles and inline text edits are temporary and reset when the page refreshes; styles are restored when the Komo widget is destroyed. Use **Copy CSS** to apply a rule in your site stylesheet; Komo does not write source files. Double-click text, or use **Edit text**, to edit it inline. Edits accept plain text only and change text in place: nested markup, links, and buttons keep their structure. Text marked `data-private` or `data-sensitive`, form fields, and text inside graphics, media, or frames stay read-only. An explicitly registered React `textProp` routes edits through the app's state callback. The inspector can select an iframe as one element but cannot edit its isolated contents. Elements inside open or closed shadow roots are not traversed; select the component host instead.
89
92
 
90
93
  The design inspector loads as a separate optional browser chunk when Design mode is first opened, so the default widget load does not pay its bundle cost. The package size check measures this chunk separately from the core bundle.
@@ -95,7 +98,7 @@ Design mode also takes interaction cues from established design tools: select a
95
98
 
96
99
  ### Opt-in React prop inspection
97
100
 
98
- React teams can register selected component roots for typed prop previews through the `./react` entry. A build-time TypeScript Compiler API pass generates the prop manifest from an explicitly marked function component and props type; the inspector edits only strings, numbers, booleans, and literal unions. Functions, `ReactNode`, objects, generics, nullable unions, and any unsupported type remain read-only. This metadata is a safe allowlist, not runtime discovery of every React component; `defaultProps` and every React component form are not inferred.
101
+ React teams can register selected component roots for typed prop previews through the `./react` entry. A build-time TypeScript Compiler API pass generates the prop manifest from an explicitly marked function component and props type; the inspector edits only strings, numbers, booleans, and literal unions. Functions, refs, `ReactNode`, objects, generics and unsupported types are omitted from editable controls. This metadata is a safe allowlist, not runtime discovery of every React component; `defaultProps` and every React component form are not inferred.
99
102
 
100
103
  The app provides an `onChange` callback that updates its own state. Komo previews those values through React's normal render cycle, offers undo/reset, and can prefill an explicit comment with the proposed before/after values and source path. Posting still requires the user to submit the comment. Komo's core never accesses React Fiber internals and never writes source files; the one exception is the separate, opt-in [React fiber adapter](#discovering-react-components), which reads React internals only for apps that install it. Preview prop changes reset on refresh. A component may opt one leaf render into direct text editing by setting `textProp` to the name of an allowed string prop; Komo does not infer which child text belongs to a prop. See [`examples/react-props`](./examples/react-props/README.md) for the generated fixture and limitations.
101
104
 
@@ -214,9 +217,21 @@ initKomo({ project, design: { components: discoverReactComponents() } });
214
217
  ```
215
218
 
216
219
  - **What it reads.** The `__reactFiber$*` key React puts on DOM nodes (or, when present, the renderers of `window.__REACT_DEVTOOLS_GLOBAL_HOOK__`), then `return`/`child` links, `memoizedProps`, `_debugSource` (React 18) and `_debugStack` (React 19 dev). It climbs from the picked element, through shadow hosts, to the nearest function, class, `forwardRef` or `memo` component, and picks that fiber's current copy so props are not stale after updates. Several React roots and renderers on a page are fine.
217
- - **What it shows.** The panel adds a **Discovered props** group labelled "discovered from React, read-only", with the props and "rendered at `file:line`". The location is where the component was written into JSX (the call site), not where it is defined. React 19 records an Error stack of the generated file; a dev server that serves modules unbundled gives real paths, and otherwise pass `resolveSource(file, line, column)` to map positions (for example through a source map). Proposals also name the component with its primitive props, like any component adapter.
218
- - **Props are summarized, never run.** Strings, numbers and booleans are shown; arrays and plain objects are cut off by `maxDepth` (3), `maxItems` (6), `maxString` (80) and `maxValue` (240), and `maxProps` caps the list (24). Functions, React elements, symbols, DOM nodes and class instances are described (`ƒ onSave()`, `<button />`), cycles read `[Circular]`, and getters are never invoked. Props named like secrets (`token`, `password`, `apiKey`, `secret`, `auth`…) and anything `redact(name)` matches show as `[redacted]` and are kept out of proposals.
219
- - **Read-only, and registered components win.** Discovered props are never editable, because komo has no state callback or typed schema for them. A component registered with `useKomoInspectable`, or answered by your own `adapter`, always takes precedence and keeps its editable typed props; around an element inside a registered component the panel keeps showing "Inside *Component*". A component nested inside a registered one that is not registered itself is discovered.
220
+ - **Useful props first.** Strings, finite numbers and booleans appear in **Discovered props**. Callbacks, refs, undefined values, React children, opaque objects and framework internals are omitted without invoking getters. Technical primitive attributes are in collapsed **Advanced attributes**; `aria-label` stays with useful props. Secret-looking names and `redact(name)` matches are excluded from both the panel and proposals. `maxProps` (24) and `maxString` (80) bound the display.
221
+ - **Location and precedence.** “Rendered at `file:line`” identifies the JSX call site. Use `resolveSource(file, line, column)` to map bundled locations. Explicit `useKomoInspectable` registrations and your own `adapter` win. A descendant points to its component root; props edit that instance only.
222
+ - **Runtime editing is optional.** Import `@hazem3500/komo/adapters/react-preview` before the first `react-dom/client` import, then use `discoverReactComponents({ edit: true })`. The separate bootstrap captures React 18/19 development renderer capabilities, preserves an existing DevTools hook, and calls the renderer's own prop override method. It never assigns Fiber fields. Production and unsupported renderers, or renderers whose capabilities were not captured, stay read-only. Discovered class components need an explicit editor/registration. Import order matters: calling the installer after React has loaded cannot recover missing renderer capabilities.
223
+
224
+ ```ts
225
+ // First import in the app's development entry, before its React/router graph.
226
+ import "@hazem3500/komo/adapters/react-preview";
227
+ // In the lazily enabled feedback configuration:
228
+ const components = discoverReactComponents({ edit: true });
229
+ ```
230
+
231
+ Primitive controls use current prop types. Declare optional or enum controls through `schema(identity)` when you have component metadata; runtime discovery cannot infer a union from one string. Edits stay applied through parent re-renders, suspend outside Design, and support Before/After, Undo, per-field Reset and Reset all. Reset restores the pre-edit value and releases the override; unmounting never transfers it to another instance. These are runtime previews, with proposed changes available for comments, not source edits.
232
+
233
+ For a framework-independent integration, a `ComponentAdapter` may supply `editor(identity)` returning a stable `ComponentEditor` with `schema`, `read()` and `apply(next)`. Apply through the app's state or supported renderer API, preserve other props, and keep the preview stable through re-renders. Optional `available()` rejects detached instances; `release(properties)` stops retaining overrides after baseline restoration, and `subscribe(listener)` refreshes computed controls after a render commits. Existing `useKomoInspectable` registrations remain supported and take precedence.
234
+
220
235
  - **Graceful no-op.** Every access is guarded. Non-React pages, unknown React versions or fiber shapes, production builds and hostile props yield nothing rather than an error. Production React keeps no debug fields, so there is no location; names that look minified (one or two characters) or are missing are not shown. `include(name)` hides components you don't want listed.
221
236
  - **Fragile by design.** These fields are private React implementation details, and React can rename or remove them in any release. React 18.3 and 19.2 are tested here (unit tests and a Chrome run of the demo for each); another version may quietly show nothing until the adapter is updated. If that matters more than convenience, register the components you care about with `useKomoInspectable` instead.
222
237
 
@@ -235,13 +250,13 @@ const mode = classTheme({
235
250
  });
236
251
  const product = classTheme({
237
252
  id: "product",
238
- options: [{ id: "focus", attribute: { "data-product": "focus" } }, { id: "track", attribute: { "data-product": "track" } }],
253
+ options: [{ id: "studio", attribute: { "data-product": "studio" } }, { id: "docs", attribute: { "data-product": "docs" } }],
239
254
  });
240
255
 
241
256
  // Generate this from your design-system foundations.
242
257
  const tokens: TailwindToken[] = [
243
258
  { path: "color.text.primary", type: "color", class: "text-primary", properties: ["color"], group: "Text",
244
- values: { light: "#1f1f1f", dark: "#f5f5f5", "track+dark": "#fce5d8" } },
259
+ values: { light: "#1f1f1f", dark: "#f5f5f5", "docs+dark": "#fce5d8" } },
245
260
  { path: "color.bg.primary-interactive", type: "color", class: "bg-primary-interactive",
246
261
  properties: ["background-color"], value: "#e57cd8", states: ["hover", "active", "disabled"] },
247
262
  { path: "radius.md", type: "dimension", class: "rounded-md", properties: ["border-radius"], value: "6px" },
@@ -1,7 +1,11 @@
1
- import type { ComponentAdapter } from "../design-system.js";
1
+ import type { ComponentAdapter, ComponentIdentity, ComponentProperty } from "../design-system.js";
2
2
  export interface DiscoverReactComponentsOptions {
3
3
  /** The app's own adapter, asked first; this adapter answers only when it names nothing. */
4
4
  adapter?: ComponentAdapter;
5
+ /** Enable runtime editing when the optional react-preview bridge supports this renderer. */
6
+ edit?: boolean;
7
+ /** Declare enums or optional props that discovery cannot infer from their current values. */
8
+ schema?: (identity: ComponentIdentity) => ComponentProperty[] | undefined;
5
9
  /** Component names to show; return false to skip a component (e.g. library wrappers). */
6
10
  include?: (name: string) => boolean;
7
11
  /**
@@ -14,13 +18,13 @@ export interface DiscoverReactComponentsOptions {
14
18
  redact?: (name: string) => boolean;
15
19
  /** Most props listed; default 24. */
16
20
  maxProps?: number;
17
- /** Nesting depth of objects and arrays shown; default 3. */
21
+ /** @deprecated Opaque values are no longer displayed. */
18
22
  maxDepth?: number;
19
- /** Most array items or object keys shown per level; default 6. */
23
+ /** @deprecated Opaque values are no longer displayed. */
20
24
  maxItems?: number;
21
25
  /** Longest string shown; default 80. */
22
26
  maxString?: number;
23
- /** Most characters in one summarized prop value; default 240. */
27
+ /** @deprecated Only bounded primitive values are displayed. */
24
28
  maxValue?: number;
25
29
  }
26
30
  /** A `ComponentAdapter` that finds the nearest React component for a picked element. */
@@ -1 +1 @@
1
- var e=new Set([0,1,11,15]),t=["react.element","react.transitional.element"],n=/pass(word|wd)?|secret|token|auth|api[-_]?key|credential|cookie|session|private/i,r=/^[a-z_$][\w$]?$/i,l=new WeakMap,a=e=>{try{return e()}catch{return}},i=(e,t)=>{let n=String(e).replace(/\s+/g," ");return n.length>t?`${n.slice(0,t)}…`:n},o=e=>!!e&&"object"==typeof e;function s(e){for(let t of Object.keys(e))if(t.startsWith("__reactFiber$")){let n=e[t];if(o(n))return n}let t=a(()=>globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__);for(let n of t?.renderers?.values?.()??[]){let t=a(()=>n.findFiberByHostInstance?.(e));if(o(t))return t}return null}function u(e,t=0){if("function"==typeof e){let t=e,n="string"==typeof t.displayName&&t.displayName?t.displayName:t.name;return"string"==typeof n&&n?n:void 0}if(e&&"object"==typeof e&&t<3){let n=e;return"string"==typeof n.displayName&&n.displayName?n.displayName:u(n.render??n.type,t+1)}}function c(e){let t=e.child;for(let e=0;t&&e<2e3;e++){if(5===t.tag)return t.stateNode instanceof Element?t.stateNode:null;t=t.child??t.sibling}return null}var d=/jsx[-_](dev[-_])?runtime|react-stack|react[-_.]development|node_modules[\\/](\.[\w-]+[\\/])*(react|react-dom|scheduler)[@\\/]/,p=/(?:\(|@|\bat\s+|^\s*)((?:[a-z][a-z0-9+.-]*:\/\/|\/|[a-z]:\\)[^()]*?):(\d+):(\d+)\)?\s*$/i;function m(e,t,n,r){let l=(e=>e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*/i,"").replace(/[?#].*$/,"").replace(/^\/@fs/,"").replace(/%20/g," "))(e);if(!l||!t)return;let o=a(()=>r?r(l,t,n):`${l}:${t}`);return"string"==typeof o&&o?i(o,160):void 0}function f(e,t){let n=e._debugSource;if(o(n)&&"string"==typeof n.fileName)return m(n.fileName,Number(n.lineNumber)||0,Number(n.columnNumber)||0,t);let r=a(()=>e._debugStack?.stack);return"string"==typeof r?function(e,t){let n=0;for(let r of e.split("\n").slice(0,40)){let e=d.test(r)?null:p.exec(r.trim());if(!e||++n>8)continue;let l=m(e[1],Number(e[2]),Number(e[3]),t);if(l&&!d.test(l))return l}}(r,t):void 0}function g(e,n,r=[]){switch(typeof e){case"string":return JSON.stringify(i(e,n.string));case"number":case"boolean":case"undefined":return String(e);case"bigint":return`${e}n`;case"symbol":return a(()=>String(e))??"Symbol()";case"function":return`ƒ ${a(()=>u(e))??"anonymous"}()`}if(null===e)return"null";let l=e;if(r.includes(l))return"[Circular]";if((e=>t.some(t=>e.$$typeof===Symbol.for(t)))(l)){let e=l.type;return`<${"string"==typeof e?e:a(()=>u(e))??"Component"} />`}if(typeof Node<"u"&&l instanceof Node)return`<${l.localName??"node"}>`;if(Array.isArray(l)){if(r.length>=n.depth)return`Array(${l.length})`;let e=[];for(let t=0;t<Math.min(l.length,n.items);t++){let a=Object.getOwnPropertyDescriptor(l,t);e.push(a&&"value"in a?g(a.value,n,[...r,l]):"[getter]")}return`[${e.join(", ")}${l.length>n.items?", … +"+(l.length-n.items):""}]`}let o=Object.getPrototypeOf(l);if(o!==Object.prototype&&null!==o){let e=a(()=>u(o.constructor))??"Object";return"Date"===e?a(()=>l.toISOString())??"Date":"Map"===e||"Set"===e?`${e}(${a(()=>l.size)??"?"})`:"RegExp"===e?i(a(()=>String(l))??"RegExp",n.string):`${e} {…}`}let s=a(()=>Object.keys(l))??[];return s.length?r.length>=n.depth?"{…}":`{ ${s.slice(0,n.items).map(e=>{let t=Object.getOwnPropertyDescriptor(l,e);return`${i(e,40)}: ${t&&"value"in t?g(t.value,n,[...r,l]):"[getter]"}`}).join(", ")}${s.length>n.items?", … +"+(s.length-n.items):""} }`:"{}"}function y(e,t){let n=document.createElement("label");n.className="design-field design-react-field";let r=document.createElement("span");r.textContent=e,r.title=e;let l=document.createElement("input");return l.value=t,l.title=t,l.disabled=!0,l.setAttribute("aria-label",`${e} (discovered, read-only)`),n.append(r,l),n}function h(t={}){let d={depth:Math.max(0,t.maxDepth??3),items:Math.max(1,t.maxItems??6),string:Math.max(8,t.maxString??80),value:Math.max(40,t.maxValue??240)},p=Math.max(1,t.maxProps??24),m=e=>n.test(e)||!!a(()=>t.redact?.(e));function h(n){let y=function(e){let t=e;for(let e=0;t&&e<2e3;e++){if(1===t.nodeType){let e=s(t);if(e)return e}t=t.parentNode??t.host??null}return null}(n);if(!y)return null;let h=y;for(let t=0;h&&t<5e3&&(void 0===h.tag||!e.has(h.tag));t++)h=h.return;if(!h||!e.has(h.tag))return null;h=function(e){let t=[];for(let n=e;n&&t.length<5e3;n=n.return)t.push(n);let n=t[t.length-1],r=3===n?.tag&&o(n.stateNode)?n.stateNode.current:void 0;if(!o(r))return e;let l=r;for(let n=t.length-2;n>=0;n--){let r=l.child;for(;r&&r!==t[n]&&r!==t[n].alternate;)r=r.sibling;if(!r)return e;l=r}return l}(h);let b=u(h.type);if(!b||r.test(b)||t.include&&!t.include(b))return null;let $=o(h.memoizedProps)?h.memoizedProps:{},N=a(()=>Object.keys($))??[],v=[],x={};for(let e of N.slice(0,p)){let t=Object.getOwnPropertyDescriptor($,e);if(!t||!("value"in t))continue;let n=t.value;m(e)?v.push([e,"[redacted]"]):(v.push([e,i(a(()=>g(n,d))??"[unreadable]",d.value)]),(null===n||["string","number","boolean"].includes(typeof n))&&(x[e]=n))}let O=f(h,t.resolveSource),_={name:i(b,80),props:x,element:c(h)??n,...O?{source:O}:{}};return l.set(_,{props:v,hidden:Math.max(0,N.length-p),location:O}),_}return{identify(e){try{let n=a(()=>t.adapter?.identify(e));return n?.name?n:h(e)}catch{return null}},panel(e){let n=l.get(e);if(!n)return a(()=>t.adapter?.panel?.(e))??null;let r=document.createElement("details");r.className="design-group design-react-group design-react-discovered",r.open=!0;let i=document.createElement("summary");i.textContent="Discovered props";let o=document.createElement("small");if(o.className="design-react-source",o.textContent=`${e.name} · discovered from React, read-only${n.location?` · rendered at ${n.location}`:""}`,r.append(i,o),!n.props.length){let e=document.createElement("small");e.className="design-react-source",e.textContent="No props.",r.append(e)}for(let[e,t]of n.props)r.append(y(e,t));if(n.hidden){let e=document.createElement("small");e.className="design-react-source",e.textContent=`+${n.hidden} more props not shown`,r.append(e)}return r}}}export{h as discoverReactComponents};
1
+ var e=new Set([0,1,11,15]),t=/pass(word|wd)?|secret|token|auth|api[-_]?key|credential|cookie|session|private/i,n=/^[a-z_$][\w$]?$/i,r=e=>/^(key|ref|children|dangerouslySetInnerHTML|suppressHydrationWarning)$|^on[A-Z]|^_|^\$\$/.test(e),l=e=>/^(className|id|role|tabIndex|asChild)$|^(data-|aria-)/.test(e)&&"aria-label"!==e,a=new WeakMap,i=e=>{try{return e()}catch{return}},o=(e,t)=>{let n=String(e).replace(/\s+/g," ");return n.length>t?`${n.slice(0,t)}…`:n},d=e=>!!e&&"object"==typeof e;function s(e){for(let t of Object.keys(e))if(t.startsWith("__reactFiber$")){let n=e[t];if(d(n))return n}let t=i(()=>globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__);for(let n of t?.renderers?.values?.()??[]){let t=i(()=>n.findFiberByHostInstance?.(e));if(d(t))return t}return null}function u(e){let t=[];for(let n=e;n&&t.length<5e3;n=n.return)t.push(n);let n=t[t.length-1],r=3===n?.tag&&d(n.stateNode)?n.stateNode.current:void 0;if(!d(r))return e;let l=r;for(let n=t.length-2;n>=0;n--){let r=l.child;for(;r&&r!==t[n]&&r!==t[n].alternate;)r=r.sibling;if(!r)return e;l=r}return l}function c(e,t=0){if("function"==typeof e){let t=e,n="string"==typeof t.displayName&&t.displayName?t.displayName:t.name;return"string"==typeof n&&n?n:void 0}if(e&&"object"==typeof e&&t<3){let n=e;return"string"==typeof n.displayName&&n.displayName?n.displayName:c(n.render??n.type,t+1)}}function p(e){let t=e.child;for(let e=0;t&&e<2e3;e++){if(5===t.tag)return t.stateNode instanceof Element?t.stateNode:null;t=t.child??t.sibling}return null}var f=/jsx[-_](dev[-_])?runtime|react-stack|react[-_.]development|node_modules[\\/](\.[\w-]+[\\/])*(react|react-dom|scheduler)[@\\/]/,m=/(?:\(|@|\bat\s+|^\s*)((?:[a-z][a-z0-9+.-]*:\/\/|\/|[a-z]:\\)[^()]*?):(\d+):(\d+)\)?\s*$/i;function g(e,t,n,r){let l=(e=>e.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]*/i,"").replace(/[?#].*$/,"").replace(/^\/@fs/,"").replace(/%20/g," "))(e);if(!l||!t)return;let a=i(()=>r?r(l,t,n):`${l}:${t}`);return"string"==typeof a&&a?o(a,160):void 0}function b(e,t){let n=e._debugSource;if(d(n)&&"string"==typeof n.fileName)return g(n.fileName,Number(n.lineNumber)||0,Number(n.columnNumber)||0,t);let r=i(()=>e._debugStack?.stack);return"string"==typeof r?function(e,t){let n=0;for(let r of e.split("\n").slice(0,40)){let e=f.test(r)?null:m.exec(r.trim());if(!e||++n>8)continue;let l=g(e[1],Number(e[2]),Number(e[3]),t);if(l&&!f.test(l))return l}}(r,t):void 0}function y(e,t){let n=document.createElement("label");n.className="design-field design-react-field";let r=document.createElement("span");r.textContent=e,r.title=e;let l=document.createElement("input");return l.value=t,l.title=t,l.readOnly=!0,l.setAttribute("aria-label",`${e} (discovered, read-only)`),n.append(r,l),n}function h(f={}){let m=Math.max(8,f.maxString??80),g=new WeakMap,h=Math.max(1,f.maxProps??24),v=e=>t.test(e)||!!i(()=>f.redact?.(e));function N(t){let g=function(e){let t=e;for(let e=0;t&&e<2e3;e++){if(1===t.nodeType){let e=s(t);if(e)return e}t=t.parentNode??t.host??null}return null}(t);if(!g)return null;let y=g;for(let t=0;y&&t<5e3&&(void 0===y.tag||!e.has(y.tag));t++)y=y.return;if(!y||!e.has(y.tag))return null;y=u(y);let N=c(y.type);if(!N||n.test(N)||f.include&&!f.include(N))return null;let _=d(y.memoizedProps)?y.memoizedProps:{},$=i(()=>Object.keys(_))??[],O=[],E={},k=[],x=0;for(let e of $){let t=Object.getOwnPropertyDescriptor(_,e);if(!t||!("value"in t))continue;let n=t.value;if(v(e)||r(e))continue;if(null===n){l(e)||(E[e]=null);continue}if(void 0===n||!("string"==typeof n||"boolean"==typeof n||"number"==typeof n&&Number.isFinite(n)))continue;if((l(e)?k:O).length>=h){x++;continue}let a="string"==typeof n?JSON.stringify(o(n,m)):String(n);l(e)?k.push([e,a]):(O.push([e,a]),E[e]=n)}let w=b(y,f.resolveSource),S={name:o(N,80),props:E,element:p(y)??t,...w?{source:w}:{}};return a.set(S,{props:O,advanced:k,hidden:x,location:w,fiber:y}),S}return{identify(e){try{let t=i(()=>f.adapter?.identify(e));return t?.name?t:N(e)}catch{return null}},editor(e){let t=a.get(e);if(!t)return i(()=>f.adapter?.editor?.(e))??null;if(!f.edit||1===t.fiber.tag)return null;let n=g.get(e.element);if(n&&(n.fiber===t.fiber||n.fiber.alternate===t.fiber)&&!1!==n.editor.available?.())return n.editor;let l=(i(()=>f.schema?.(e))??Object.entries(e.props??{}).filter(([,e])=>null!==e).map(([e,t])=>({name:e,kind:typeof t,required:!0,typeLabel:typeof t}))).filter(e=>!v(e.name)&&!r(e.name)&&"unsupported"!==e.kind),o=()=>i(()=>{if(!e.element.isConnected)return null;let n=u(t.fiber),r=n;for(let e=0;r.return&&e<5e3;e++)r=r.return;let l=r.stateNode;return 3!==r.tag||l?.current!==r||p(n)!==e.element?null:{fiber:n,root:l,props:n.memoizedProps??{}}})??null,d=globalThis.__REACT_DEVTOOLS_GLOBAL_HOOK__,s=i(()=>d?.[Symbol.for("inline-feedback.react-preview")]?.editor({schema:l,current:o}));return s&&g.set(e.element,{fiber:t.fiber,editor:s}),s??null},panel(e){let t=a.get(e);if(!t)return i(()=>f.adapter?.panel?.(e))??null;let n=document.createElement("details");n.className="design-group design-react-group design-react-discovered",n.open=!0;let r=document.createElement("summary");r.textContent="Discovered props";let l=document.createElement("small");if(l.className="design-react-source",l.textContent=`${e.name} · read-only${t.location?` · rendered at ${t.location}`:""}`,l.title=f.edit?"Runtime editing needs the React preview bridge loaded before a supported development renderer, or a component editor adapter.":"Enable a component editor adapter or React runtime previews to edit these props.",n.append(r,l),!t.props.length){let e=document.createElement("small");e.className="design-react-source",e.textContent="No props.",n.append(e)}for(let[e,r]of t.props)n.append(y(e,r));if(t.advanced.length){let e=document.createElement("details");e.className="design-react-advanced";let r=document.createElement("summary");r.textContent="Advanced attributes",e.append(r);for(let[n,r]of t.advanced)e.append(y(n,r));n.append(e)}if(t.hidden){let e=document.createElement("small");e.className="design-react-source",e.textContent=`+${t.hidden} more props not shown`,n.append(e)}return n}}}export{h as discoverReactComponents};
@@ -0,0 +1,16 @@
1
+ import type { ComponentEditor, ComponentProperty } from "../design-system.js";
2
+ interface Snapshot {
3
+ fiber: object;
4
+ root: object;
5
+ props: Record<string, unknown>;
6
+ }
7
+ export interface ReactPreviewRequest {
8
+ schema: ComponentProperty[];
9
+ current(): Snapshot | null;
10
+ }
11
+ export declare const reactPreviewKey: unique symbol;
12
+ export interface ReactPreviewBridge {
13
+ editor(request: ReactPreviewRequest): ComponentEditor | null;
14
+ }
15
+ export declare function installReactPreview(): boolean;
16
+ export {};
@@ -0,0 +1 @@
1
+ var e=e=>Object.defineProperties({},Object.fromEntries(Object.entries(Object.getOwnPropertyDescriptors(e)).map(([e,r])=>[e,{...r,configurable:!0,..."value"in r?{writable:!0}:{}}]))),r=Symbol.for("inline-feedback.react-preview");function t(){try{return function(){if(typeof window>"u")return!1;let t=globalThis,o=t.__REACT_DEVTOOLS_GLOBAL_HOOK__;if(o?.[r])return!0;if(!o){let e=new Map,r=new Map;o={supportsFiber:!0,renderers:e,inject(r){let t=e.size+1;return e.set(t,r),t},getFiberRoots(e){let t=r.get(e);return t||r.set(e,t=new Set),t}};try{t.__REACT_DEVTOOLS_GLOBAL_HOOK__=o}catch{return!1}}if(!o.supportsFiber||!o.renderers)return!1;let i=new Set,n=new Set,l=new WeakMap;for(let[e,r]of o.renderers)for(let t of o.getFiberRoots?.(e)??[])l.set(t,r);let s=o.onCommitFiberRoot,a=!1,f=function(e,r,...t){try{s?.call(this,e,r,...t)}finally{let t=o.renderers.get(e);t&&l.set(r,t),n.add(r),i.size&&!a?(a=!0,queueMicrotask(()=>{a=!1;let e=[...n];n.clear();for(let r of e)for(let e of[...i])e(r)})):i.size||n.clear()}};try{o.onCommitFiberRoot=f}catch{return!1}if(o.onCommitFiberRoot!==f)return!1;let c={editor(r){let t=r.current(),o=t&&l.get(t.root);if(!o?.overrideProps||1!==o.bundleType||"react-dom"!==o.rendererPackageName||!/^(18|19)\./.test(o.version??""))return null;let n,s=new Map,a=new Set,f=()=>r.current(),c=r=>{let t=f();if(!t)return i.delete(c),s.clear(),void(n=void 0);if(t.root===r){if(n=void 0,s.size){let r=e(t.props),i=!1;for(let[e,t]of s)Object.is(r[e],t.value)||(r[e]=t.value,i=!0);if(i)return void o.overrideProps(t.fiber,[],r)}for(let e of a)e();!s.size&&!a.size&&i.delete(c)}};return{schema:r.schema,available:()=>!!f(),release(e){for(let r of e)s.delete(r)},subscribe:e=>(a.add(e),i.add(c),()=>{a.delete(e),!a.size&&!s.size&&!n&&i.delete(c)}),read(){let e={},t=n??f()?.props??{};for(let o of r.schema)e[o.name]=Object.getOwnPropertyDescriptor(t,o.name)?.value;return e},apply(t){let l=f();if(!l)return;let a=n??l.props,u=e(l.props);for(let e of r.schema){let r=e.name,o=t[r],i=s.get(r);!i&&!Object.is(a[r],o)&&(i={before:a[r],present:Object.hasOwn(a,r),value:o},s.set(r,i)),i&&(i.value=o,u[r]=o,Object.is(o,i.before)&&(i.present||delete u[r],s.delete(r)))}n=u,i.add(c),o.overrideProps(l.fiber,[],u)}}}};try{o[r]=c}catch{return o.onCommitFiberRoot=s,!1}return!0}()}catch{return!1}}t();export{t as installReactPreview,r as reactPreviewKey};
@@ -0,0 +1 @@
1
+ import{a as e}from"./chunk-RPY7OOOR.js";var t=new WeakMap,r=new WeakMap,n=new WeakMap;function o(e,t){let r=n.get(e.element);if(r?.editor===t)return r.component;let o={id:e.name,name:e.name,source:e.source??"",line:0,props:t.schema},i={definition:o,preview:"adapter",releaseProps:e=>t.release?.(e),subscribe:t.subscribe?.bind(t),get props(){return l(o,t.read())},onChange:e=>t.apply(e),setProps(e){if(!1===t.available?.())return;let r=l(o,e);if(Object.keys(r).length!==Object.keys(e).length||o.props.some(e=>"unsupported"!==e.kind&&e.required&&!(e.name in r)))throw new Error("Component props no longer match the editor schema.");t.apply(r)}};return n.set(e.element,{editor:t,component:i}),i}function i(e,t){return void 0===t?!e.required:!(!function(e){return void 0===e||"string"==typeof e||"number"==typeof e&&Number.isFinite(e)||"boolean"==typeof e}(t)||"unsupported"===e.kind)&&("string"===e.kind?"string"==typeof t&&t.length<=500:"number"===e.kind?"number"==typeof t&&Number.isFinite(t):"boolean"===e.kind?"boolean"==typeof t:"enum"===e.kind&&(e.values??[]).includes(t))}function l(e,t){let r={};for(let n of e.props){if("unsupported"===n.kind)continue;let e=t[n.name];i(n,e)&&(r[n.name]=e)}return r}function s(e,n,o,i){let s=n.textProp&&n.props.some(e=>e.name===n.textProp&&"string"===e.kind)?n.textProp:void 0,a=l(n,o),p=new Set,u={definition:n,props:a,onChange:i,subscribe:e=>(p.add(e),()=>{p.delete(e)}),setProps(e){let t=l(n,e);if(Object.keys(t).length===Object.keys(e).length){for(let e of n.props)if("unsupported"!==e.kind&&e.required&&!(e.name in t))return;u.props=t,u.onChange(t)}}};return t.set(e,u),e.dataset.komoInspectable=n.id,s?e.dataset.komoTextProp=s:delete e.dataset.komoTextProp,r.set(u,p),()=>{t.get(e)===u&&(t.delete(e),p.clear(),e.dataset.komoInspectable===n.id&&delete e.dataset.komoInspectable,e.dataset.komoTextProp===s&&delete e.dataset.komoTextProp)}}function a(e,n,o,i){if(!e)return;let s=t.get(e);if(!s||s.definition.id!==n.id)return;s.definition=n,s.props=l(n,o),s.onChange=i;let a=n.textProp&&n.props.some(e=>e.name===n.textProp&&"string"===e.kind)?n.textProp:void 0;a?e.dataset.komoTextProp=a:delete e.dataset.komoTextProp,r.get(s)?.forEach(e=>e())}function p(e){let r=e&&t.get(e);if(r&&e.getAttribute("data-komo-inspectable")===r.definition.id)return r;let o=e&&n.get(e);return o&&!1!==o.editor.available?.()?o.component:null}function u(e){for(let t=e?.parentElement;t;t=t.parentElement){let e=p(t);if(e)return{root:t,component:e}}return null}var f=new Set,d=new WeakMap,m=new WeakMap,c=new WeakMap,h={color:new Set(["color","background-color","border-color"]),dimension:new Set(["gap","padding","margin","border-radius","font-size","width","height"]),fontFamily:new Set(["font-family"]),fontWeight:new Set(["font-weight"]),lineHeight:new Set(["line-height"])};function g(e,t){if(t.length>100||t.some(e=>!function(e){return!!e&&"string"==typeof e.path&&e.path.length<=160&&/^--[a-zA-Z0-9_-]+$/.test(e.cssVariable)&&!!h[e.type]&&Array.isArray(e.properties)&&e.properties.length>0&&e.properties.every(t=>h[e.type].has(t))&&"string"==typeof e.impactSelector&&e.impactSelector.length<=200}(e)))throw new Error("Komo design tokens must use supported, explicit CSS-variable metadata");let r=new Set(t.map(e=>e.path)),n=new Set(t.map(e=>e.cssVariable));if(r.size!==t.length||n.size!==t.length)throw new Error("Komo design token paths and CSS variables must be unique");for(let r of t)e.querySelectorAll(r.impactSelector);let o={root:e,tokens:t.map(e=>({...e,properties:[...e.properties]})),themeEdits:new Map,elementOriginals:new Map,elementBindings:new Map,previousMarker:e.getAttribute("data-komo-design-tokens")};return d.set(e,o),f.add(o),c.set(o,[]),e.setAttribute("data-komo-design-tokens",String(t.length)),()=>{if(d.get(e)===o){for(let[t,r]of o.themeEdits){let n=o.tokens.find(e=>e.path===t);n&&(r.inlineBefore?e.style.setProperty(n.cssVariable,r.inlineBefore):e.style.removeProperty(n.cssVariable))}for(let[e,t]of o.elementOriginals)for(let[r,n]of t)n.value?e.style.setProperty(r,n.value,n.priority):e.style.removeProperty(r);o.elementBindings.clear(),f.delete(o),d.delete(e),null===o.previousMarker?e.removeAttribute("data-komo-design-tokens"):e.setAttribute("data-komo-design-tokens",o.previousMarker)}}}function y(e){if(!e)return null;let t=null;for(let r of f)(r.root===e||r.root.contains(e))&&(!t||t.root.contains(r.root))&&(t=r);return t}function k(e,t){return e.root.ownerDocument.defaultView?.getComputedStyle(e.root).getPropertyValue(t.cssVariable).trim()??""}function b(e,t){try{return e.root.querySelectorAll(t.impactSelector).length}catch{return 0}}function P(e,t,r,n=!1){if(!e.tokens.some(e=>e===t||e.path===t.path)||!function(e,t,r){if(!r||r.length>160||/[{};]/.test(r)||/(?:url|expression)\s*\(/i.test(r))return!1;let n=e.root.ownerDocument.defaultView?.CSS;return!n?.supports||t.properties.some(e=>n.supports(e,r))}(e,t,r))return!1;let o=k(e,t),i=e.themeEdits.get(t.path),l=e.root.style.getPropertyValue(t.cssVariable),s=i?.inlineBefore??l,a=c.get(e)??[];if(o===r)return!0;e.root.style.setProperty(t.cssVariable,r),r===(i?.before??o)?e.themeEdits.delete(t.path):e.themeEdits.set(t.path,{before:i?.before??o,after:r,inlineBefore:s});let p=a.at(-1);return n&&"theme"===p?.kind&&p.token.path===t.path?r===p.before?a.pop():p.after=r:a.push({kind:"theme",source:e,token:t,before:o,after:r,inlineBefore:l}),c.set(e,a),!0}function v(e){for(let t of f)for(let[r,n]of t.themeEdits){let o=t.tokens.find(e=>e.path===r)?.cssVariable;if(!o)continue;let i=e?n.inlineBefore:n.after;i?t.root.style.setProperty(o,i):t.root.style.removeProperty(o)}}function w(e,t){let r=e.themeEdits.get(t.path),n=c.get(e)??[],o=n.find(e=>"theme"===e.kind&&e.token.path===t.path),i=r?.inlineBefore??("theme"===o?.kind?o.inlineBefore:void 0);void 0!==i&&(i?e.root.style.setProperty(t.cssVariable,i):e.root.style.removeProperty(t.cssVariable),e.themeEdits.delete(t.path),c.set(e,n.filter(e=>"theme"!==e.kind||e.token.path!==t.path)))}function V(e,t,r,n,o={}){if(!e.tokens.some(e=>e.path===r.path)||!r.properties.includes(n))return!1;let i=e.elementOriginals.get(t);i||e.elementOriginals.set(t,i=new Map),i.has(n)||i.set(n,{value:t.style.getPropertyValue(n),priority:t.style.getPropertyPriority(n)});let l=t.style.getPropertyValue(n),s=t.style.getPropertyPriority(n),a=`var(${r.cssVariable})`,p=e.elementBindings.get(t);p||e.elementBindings.set(t,p=new Map);let u=p.get(n);if(p.set(n,{tokenPath:r.path,initialValue:u?.initialValue??l,initialPriority:u?.initialPriority??s,mode:"token",declaredPath:u?u.declaredPath:A(e,t,n)?.token.path}),t.style.setProperty(n,a,s),!1===o.recordHistory)return!0;let f={kind:"element",element:t,property:n,before:l,priority:s,after:a},d=c.get(e)??[];d.push(f),c.set(e,d);let h=m.get(t)??[];return h.push(f),m.set(t,h),!0}function B(e,t,r,n){let o=e.elementBindings.get(t),i=o?.get(r);if(!i){let n=A(e,t,r);if(!n)return;o||e.elementBindings.set(t,o=new Map),i={tokenPath:n.token.path,initialValue:t.style.getPropertyValue(r),initialPriority:t.style.getPropertyPriority(r),mode:"custom",declaredPath:n.token.path},o.set(r,i)}i.mode="custom",i.customValue=n}function S(e,t,r){let n=e.elementBindings.get(t)?.get(r);if(!n)return;let o=t.style.getPropertyValue(r);o!==n.initialValue||t.style.getPropertyPriority(r)!==n.initialPriority?e.tokens.find(e=>e.path===n.tokenPath&&e.properties.includes(r)&&o===`var(${e.cssVariable})`)?(n.mode="token",delete n.customValue):(n.mode="custom",n.customValue=o):e.elementBindings.get(t)?.delete(r)}function $(e,t,r){let n=e.elementBindings.get(t)?.get(r);return n?{...n}:null}var E={"background-color":["background"],"border-color":["border"],"font-family":["font"],"font-size":["font"],"font-weight":["font"],"line-height":["font"]};function x(e,t,r,n){let o=[...r.matchAll(/var\(\s*(--[\w-]+)/g)].map(e=>e[1]);if(!o.length||n&&!/^var\(\s*--[\w-]+\s*(?:,[^;{}]*)?\)$/.test(r.trim()))return;let i=e.tokens.filter(e=>e.properties.includes(t)&&o.includes(e.cssVariable));return 1===i.length?i[0]:void 0}function M(e){let t=e.replace(/:(?:where)\([^)]*\)/g,"").replace(/"[^"]*"|'[^']*'/g,"");return 1e4*(t.match(/#[\w-]+/g)?.length??0)+100*(t.match(/\.[\w-]+|\[[^\]]*\]|:(?!:)[\w-]+/g)?.length??0)+(t.match(/(?:^|[\s>+~(])[a-z][\w-]*/gi)?.length??0)}function O(e){let t=[],r=0,n=0;for(let o=0;o<e.length;o++){let i=e[o];"("===i||"["===i?r++:")"===i||"]"===i?r--:","===i&&0===r&&(t.push(e.slice(n,o)),n=o+1)}return t.push(e.slice(n)),t.map(e=>e.trim()).filter(Boolean)}function A(e,t,r,n=t.style.getPropertyValue(r),o=t.style.getPropertyPriority(r)){let i=t.ownerDocument.defaultView,l=null,s=0,a=e=>{for(let n of e){s++;let e=n.media;if(e&&i?.matchMedia&&e.mediaText&&!i.matchMedia(e.mediaText).matches)continue;let o=n.style,p=n.selectorText;if(o&&p)for(let e of[r,...E[r]??[]]){let r=o.getPropertyValue(e).trim();if(!r)continue;let n=-1;for(let e of O(p))try{t.matches(e)&&(n=Math.max(n,M(e)))}catch{}if(!(n<0)){n+=o.getPropertyPriority(e)?1e9:0,(!l||n>l.rank||n===l.rank&&s>=l.order)&&(l={value:r,property:e,rank:n,order:s});break}}let u=n.cssRules;u&&!o&&a(u)}};for(let e of t.ownerDocument.styleSheets)try{e.disabled||a(e.cssRules)}catch{}let p=l;if(n&&(!(p&&p.rank>=1e9)||o)){let t=x(e,r,n,!0);return t?{token:t,origin:"inline"}:null}if(!p)return null;let u=x(e,r,p.value,p.property===r);return u?{token:u,origin:"stylesheet"}:null}function T(e,t,r){let n=e.elementBindings.get(t)?.get(r),o=e.elementOriginals.get(t)?.get(r),i=n?.initialValue??o?.value??"",l=n?.initialPriority??o?.priority??"";i?t.style.setProperty(r,i,l):t.style.removeProperty(r),e.elementBindings.get(t)?.delete(r)}function C(e,t){return e.tokens.filter(e=>e.properties.includes(t))}function z(e){let t=y(e);return!!t&&(t.themeEdits.size>0||!!m.get(e)?.length||!!t.elementBindings.get(e)?.size)}function j(e){let t=y(e);return!!t&&!!c.get(t)?.length}function q(e){let t=y(e),r=t&&c.get(t)?.pop();if(!t||!r)return;if("theme"===r.kind){r.inlineBefore?t.root.style.setProperty(r.token.cssVariable,r.inlineBefore):t.root.style.removeProperty(r.token.cssVariable);let e=t.themeEdits.get(r.token.path);if(e&&e.after===r.after)e.before===r.before?t.themeEdits.delete(r.token.path):t.themeEdits.set(r.token.path,{...e,after:r.before});else if(!e){let e=c.get(t).find(e=>"theme"===e.kind&&e.token.path===r.token.path);t.themeEdits.set(r.token.path,{before:r.after,after:r.before,inlineBefore:("theme"===e?.kind?e:r).inlineBefore})}return}r.before?r.element.style.setProperty(r.property,r.before,r.priority):r.element.style.removeProperty(r.property);let n=m.get(r.element)??[];n.pop(),n.length||m.delete(r.element)}function W(e){let t=y(e);if(!t)return;for(let[e,r]of t.themeEdits){let n=t.tokens.find(t=>t.path===e);n&&(r.inlineBefore?t.root.style.setProperty(n.cssVariable,r.inlineBefore):t.root.style.removeProperty(n.cssVariable))}t.themeEdits.clear();let r=t.elementOriginals.get(e);for(let[t,n]of r??[])n.value?e.style.setProperty(t,n.value,n.priority):e.style.removeProperty(t);t.elementOriginals.delete(e),t.elementBindings.delete(e),m.delete(e),c.set(t,(c.get(t)??[]).filter(t=>"element"===t.kind&&t.element!==e))}function D(){for(let e of f){for(let t of[e.root,...e.elementOriginals.keys(),...e.elementBindings.keys()])W(t);c.set(e,[])}}function R(e){let t=y(e);if(!t)return;let r=[];for(let[n,o]of t.elementBindings.get(e)??[]){let e=t.tokens.find(e=>e.path===o.tokenPath&&e.properties.includes(n));if(!e)continue;let i=t.tokens.find(e=>e.path===o.declaredPath),l=i?`var(${i.cssVariable}) [${i.path}]`:o.initialValue||"(unset)";"token"===o.mode?i!==e&&r.push(`Element ${n}: ${l} → var(${e.cssVariable}) [${e.path}]${i?" [rebind]":""}`):r.push(`Element ${n}: ${l} → ${o.customValue||"(unset)"} [${i?"custom override":"custom value"}]`)}for(let n of m.get(e)??[]){if("element"!==n.kind||t.elementBindings.get(e)?.has(n.property))continue;let o=t.tokens.find(e=>n.after===`var(${e.cssVariable})`);o&&r.push(`Element ${n.property}: ${n.before||"(unset)"} → var(${o.cssVariable}) [${o.path}]`)}for(let[e,n]of t.themeEdits){let o=t.tokens.find(t=>t.path===e);o&&r.push(`Shared theme ${e} (${o.cssVariable}): ${n.before} → ${n.after}; preview scope ${o.impactSelector} (${b(t,o)} matching elements)`)}return r.length?["Proposed design-token changes (preview only)",...r,"Token references are preserved for element assignments; refresh resets previews."].join("\n"):void 0}function F(){return{id:"css-variables",list:()=>[...f].flatMap(e=>e.tokens.map(t=>function(e,t){return{path:t.path,type:t.type,value:k(e,t),properties:[...t.properties],group:t.path.split(".")[0],ref:t.cssVariable}}(e,t))),resolve(t){let r=y(t),n={};if(!r||!e(t))return n;let o=t.ownerDocument.defaultView?.getComputedStyle(t);for(let e of new Set(r.tokens.flatMap(e=>e.properties))){let i=A(r,t,e);n[e]=i?{path:i.token.path}:{offToken:o?.getPropertyValue(e).trim()??""}}return n},preview(t,r,n){let o=y(t),i=o?.tokens.find(e=>e.path===n.path);if(!o||!i||!e(t))return()=>{};let l=t.style.getPropertyValue(r),s=t.style.getPropertyPriority(r);return V(o,t,i,r,{recordHistory:!1}),()=>{l?t.style.setProperty(r,l,s):t.style.removeProperty(r),S(o,t,r)}},describe:(e,t,r,n)=>`Set ${t} to var(${n.ref})`}}export{o as a,i as b,s as c,a as d,p as e,u as f,g,y as h,k as i,b as j,P as k,v as l,w as m,V as n,B as o,S as p,$ as q,A as r,T as s,C as t,z as u,j as v,q as w,W as x,D as y,R as z,F as A};