@open-flow/ui 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/app.d.ts +2 -1
  2. package/dist/app.js +1 -1
  3. package/dist/canvas-gestures.d.ts +15 -0
  4. package/dist/canvas-gestures.js +1 -0
  5. package/dist/connect-popover.d.ts +9 -0
  6. package/dist/connect-popover.js +1 -0
  7. package/dist/cta.d.ts +17 -0
  8. package/dist/cta.js +1 -0
  9. package/dist/dev-actions.d.ts +2 -1
  10. package/dist/dev-actions.js +1 -1
  11. package/dist/dev-actions.noop.d.ts +2 -1
  12. package/dist/dev-actions.noop.js +1 -1
  13. package/dist/floating.d.ts +35 -0
  14. package/dist/floating.js +1 -0
  15. package/dist/flow-canvas.d.ts +103 -0
  16. package/dist/flow-canvas.js +1 -0
  17. package/dist/flow-edit.d.ts +61 -0
  18. package/dist/flow-edit.js +1 -0
  19. package/dist/flow-frames.d.ts +25 -0
  20. package/dist/flow-frames.js +1 -0
  21. package/dist/flow-graph.d.ts +109 -0
  22. package/dist/flow-graph.js +1 -0
  23. package/dist/flow-layout.d.ts +30 -0
  24. package/dist/flow-layout.js +1 -0
  25. package/dist/flow-start.d.ts +9 -0
  26. package/dist/flow-start.js +1 -0
  27. package/dist/frame-view.js +1 -1
  28. package/dist/frame.d.ts +21 -1
  29. package/dist/frame.js +1 -1
  30. package/dist/index.d.ts +3 -3
  31. package/dist/index.js +1 -1
  32. package/dist/journey-acts.d.ts +19 -0
  33. package/dist/journey-acts.js +1 -0
  34. package/dist/manifest.d.ts +3 -2
  35. package/dist/manifest.js +1 -1
  36. package/dist/manifest.types.d.ts +11 -1
  37. package/dist/manifest.types.js +1 -1
  38. package/dist/mirror.d.ts +6 -1
  39. package/dist/mirror.js +1 -1
  40. package/dist/node-actions.d.ts +24 -0
  41. package/dist/node-actions.js +1 -0
  42. package/dist/node-menu.d.ts +13 -0
  43. package/dist/node-menu.js +1 -0
  44. package/dist/panels/components.js +1 -1
  45. package/dist/panels/elements.js +1 -1
  46. package/dist/panels/flow-canvas-board.d.ts +49 -0
  47. package/dist/panels/flow-canvas-board.js +1 -0
  48. package/dist/panels/flow-cards.d.ts +54 -0
  49. package/dist/panels/flow-cards.js +1 -0
  50. package/dist/panels/flows.d.ts +5 -5
  51. package/dist/panels/flows.js +1 -1
  52. package/dist/pick-highlight.d.ts +26 -0
  53. package/dist/pick-highlight.js +1 -1
  54. package/dist/pin.d.ts +34 -0
  55. package/dist/screen-frame.d.ts +11 -2
  56. package/dist/screen-frame.js +1 -1
  57. package/dist/screen-sketch.d.ts +30 -0
  58. package/dist/screen-sketch.js +1 -0
  59. package/dist/screen-toolbar.d.ts +4 -3
  60. package/dist/screen-toolbar.js +1 -1
  61. package/dist/server.d.ts +2 -1
  62. package/dist/server.js +2 -1
  63. package/dist/workbench-app.d.ts +3 -2
  64. package/dist/workbench-app.js +1 -1
  65. package/package.json +1 -1
@@ -5,14 +5,23 @@ export type Zoom = "fit" | 0.5 | 0.75 | 1;
5
5
  export declare const FRAME_CHROME = 24;
6
6
  export declare const STEP_GUTTER = 50;
7
7
  export declare function fitScale(available: number, naturalWidth: number, gutters: number): number;
8
- export declare function useScale(zoom: Zoom, naturalWidth: number, gutters: number): {
8
+ export interface RowExtent {
9
+ natural: number;
10
+ gutters: number;
11
+ }
12
+ export declare function rowExtent(widths: readonly number[]): RowExtent;
13
+ export declare function framedWidth(width: number, scale: number): number;
14
+ export declare function columnWidth(width: number, scale: number): number;
15
+ export declare function fitRows(available: number, rows: readonly RowExtent[]): number;
16
+ export declare function useScale(zoom: Zoom, rows: readonly RowExtent[]): {
9
17
  ref: import("react").RefObject<HTMLDivElement | null>;
10
18
  scale: number;
11
19
  };
12
- export declare function ScreenFrame({ viewport, scale, label, children, editing, }: {
20
+ export declare function ScreenFrame({ viewport, scale, label, children, editing, bare, }: {
13
21
  viewport: Viewport;
14
22
  scale: number;
15
23
  label?: ReactNode;
16
24
  children: ReactNode;
17
25
  editing?: boolean;
26
+ bare?: boolean;
18
27
  }): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as n}from"react/jsx-runtime";import{useEffect as f,useRef as m,useState as u}from"react";import{FrameShell as b}from"./frame";import{PickBox as g}from"./pick-box";const x="data-workbench-scale",h=.15,S=24,T=50,p=8;function M(e,t,i){if(t<=0)return 1;const r=e-i-p;return r<=0?h:Math.min(1,Math.max(h,r/t))}function _(e,t,i){const r=m(null),[s,o]=u(0);f(()=>{const a=r.current;if(!a)return;const l=new ResizeObserver(d=>{o(d[0]?.contentRect.width??0)});return l.observe(a),()=>l.disconnect()},[]);const c=e==="fit"?M(s,t,i):e;return{ref:r,scale:c}}function w({viewport:e,scale:t,label:i,children:r,editing:s}){const o=Math.floor(e.width*t),c=Math.floor(e.height*t);return n("div",{style:{width:o+S},children:n(b,{label:i,meta:`${e.width}\xD7${e.height} \xB7 ${Math.round(t*100)}%`,children:n("div",{className:"bg-zinc-100 p-3 dark:bg-zinc-800",children:n(g,{enabled:s,children:n("div",{[x]:String(t),className:"relative overflow-hidden",style:{width:o,height:c},children:n("div",{className:"absolute left-0 top-0",style:{width:e.width,height:e.height,transform:`scale(${t})`,transformOrigin:"top left"},children:r})})})})})})}export{S as FRAME_CHROME,x as SCALE_ATTR,T as STEP_GUTTER,w as ScreenFrame,M as fitScale,_ as useScale};
1
+ "use client";import{jsx as i}from"react/jsx-runtime";import{useEffect as f,useRef as m,useState as x}from"react";import{EDGE_VAR as g,ZOOM_VAR as p}from"./canvas-gestures";import{FrameShell as b}from"./frame";import{PickBox as E}from"./pick-box";const M="data-workbench-scale",u=.15,h=24,R=50,S=8;function A(t,e,n){if(e<=0)return 1;const r=t-n-S;return r<=0?u:Math.min(1,Math.max(u,r/e))}function F(t){return{natural:t.reduce((e,n)=>e+n,0),gutters:t.length*h+Math.max(0,t.length-1)*R}}function l(t,e){return Math.floor(t*e)}function _(t,e){return l(t,e)+h}function v(t,e){return e.reduce((n,r)=>Math.min(n,A(t,r.natural,r.gutters)),1)}function N(t,e){const n=m(null),[r,s]=x(0);f(()=>{const c=n.current;if(!c)return;const a=new ResizeObserver(d=>{s(d[0]?.contentRect.width??0)});return a.observe(c),()=>a.disconnect()},[]);const o=t==="fit"?v(r,e):t;return{ref:n,scale:o}}function k({viewport:t,scale:e,label:n,children:r,editing:s,bare:o=!1}){const c=l(t.width,e),a=l(t.height,e);return i("div",{style:{width:_(t.width,e),[g]:`calc(1px / var(${p}, 1))`},children:i(b,{label:o?void 0:n,meta:o?void 0:`${t.width}\xD7${t.height} \xB7 ${Math.round(e*100)}%`,children:i("div",{className:"bg-zinc-100 p-3 dark:bg-zinc-800",children:i(E,{enabled:s,children:i("div",{[M]:String(e),className:"relative overflow-hidden",style:{width:c,height:a},children:i("div",{className:"absolute left-0 top-0",style:{width:t.width,height:t.height,transform:`scale(${e})`,transformOrigin:"top left"},children:r})})})})})})}export{h as FRAME_CHROME,M as SCALE_ATTR,R as STEP_GUTTER,k as ScreenFrame,_ as columnWidth,v as fitRows,A as fitScale,l as framedWidth,F as rowExtent,N as useScale};
@@ -0,0 +1,30 @@
1
+ export type SketchKind = "text" | "media" | "surface";
2
+ export interface SketchBox {
3
+ x: number;
4
+ y: number;
5
+ w: number;
6
+ h: number;
7
+ kind: SketchKind;
8
+ }
9
+ export interface Sketch {
10
+ width: number;
11
+ height: number;
12
+ boxes: SketchBox[];
13
+ }
14
+ export declare const SKETCH_MAX_BOXES = 40;
15
+ export interface SketchInput {
16
+ tag: string;
17
+ x: number;
18
+ y: number;
19
+ w: number;
20
+ h: number;
21
+ text: boolean;
22
+ }
23
+ export declare function sketchOf(viewport: {
24
+ width: number;
25
+ height: number;
26
+ }, inputs: readonly SketchInput[], max?: number): Sketch;
27
+ export declare function readSketch(doc: Document, viewport: {
28
+ width: number;
29
+ height: number;
30
+ }): Sketch;
@@ -0,0 +1 @@
1
+ const E=40,S=8e-4,g=new Set(["IMG","SVG","VIDEO","CANVAS","PICTURE"]),x=new Set(["P","H1","H2","H3","H4","H5","H6","SPAN","A","LI","LABEL","BUTTON","TD","TH","STRONG","EM","CODE","SMALL"]);function i(e){return g.has(e.tag)?"media":e.text&&x.has(e.tag)?"text":"surface"}function l(e,a,f=40){const c=e.width*e.height;if(c<=0)return{width:e.width,height:e.height,boxes:[]};const o=a.filter(t=>{if(t.w<=0||t.h<=0||t.x>=e.width||t.y>=e.height||t.x+t.w<=0||t.y+t.h<=0)return!1;const n=t.w*t.h/c,d=i(t);return n>=(d==="surface"?8e-4*4:8e-4)}).sort((t,n)=>n.w*n.h-t.w*t.h),h=[];for(const t of o){const n=(r,A)=>Math.abs(r-A)<=Math.max(2,e.width*.01);if(h.some(r=>n(r.x,t.x)&&n(r.y,t.y)&&n(r.w,t.w)&&n(r.h,t.h))||h.push(t),h.length>=f)break}const s=t=>Math.min(1,Math.max(0,t));return{width:e.width,height:e.height,boxes:h.map(t=>({x:s(t.x/e.width),y:s(t.y/e.height),w:s(t.w/e.width),h:s(t.h/e.height),kind:i(t)}))}}function y(e,a){const f=e.body;if(!f)return{width:a.width,height:a.height,boxes:[]};const c=[];for(const o of Array.from(f.querySelectorAll("*"))){const h=o.getBoundingClientRect();if(h.width<=0||h.height<=0)continue;let s=!1;for(const t of Array.from(o.childNodes))if(t.nodeType===3&&(t.textContent??"").trim().length>0){s=!0;break}c.push({tag:o.tagName.toUpperCase(),x:h.left,y:h.top,w:h.width,h:h.height,text:s})}return l(a,c)}export{E as SKETCH_MAX_BOXES,y as readSketch,l as sketchOf};
@@ -14,14 +14,15 @@ export declare function SegmentedControl<T extends string | number>({ options, v
14
14
  disabled?: boolean;
15
15
  disabledReason?: string;
16
16
  }): import("react").JSX.Element;
17
- export declare function ScreenToolbar({ viewport, onViewport, zoom, onZoom, theme, onTheme, hint, actions, identity, backHref, zoomEnabled, editing, onToggleEdit, pinCount, onOpenRequest, isolatedBase, }: {
17
+ export declare function ScreenToolbar({ viewport, onViewport, zoom, onZoom, theme, onTheme, hint, zoomDisabledReason, actions, identity, backHref, zoomEnabled, editing, onToggleEdit, pinCount, onOpenRequest, isolatedBase, }: {
18
18
  viewport: string;
19
19
  onViewport: (next: string) => void;
20
- zoom: Zoom;
21
- onZoom: (next: Zoom) => void;
20
+ zoom?: Zoom;
21
+ onZoom?: (next: Zoom) => void;
22
22
  theme: ThemeMode;
23
23
  onTheme: (next: ThemeMode) => void;
24
24
  hint?: ReactNode;
25
+ zoomDisabledReason?: string;
25
26
  actions?: ReactNode;
26
27
  identity?: ReactNode;
27
28
  backHref?: string;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as c}from"react/jsx-runtime";import{useViewerConfig as w}from"./config-context";import{cx as d}from"./cx";import{workbenchUrl as C}from"./manifest";const N="rounded-md border border-zinc-300 px-2 py-1 text-[11px] font-medium text-zinc-600 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800",j="auto";function s({options:n,value:a,onChange:o,disabled:t=!1,disabledReason:i}){return e("div",{className:d("flex rounded-md border border-zinc-300 p-0.5 dark:border-zinc-700",t&&"opacity-40"),title:t?i:void 0,children:n.map(r=>e("button",{type:"button",disabled:t,onClick:()=>o(r.id),title:t?i:r.note??void 0,className:d("rounded px-2 py-1 text-[11px] font-medium transition-colors",r.id===a?"bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:r.label},String(r.id)))})}function b({label:n,children:a}){return c("span",{className:"flex items-center gap-1.5",children:[e("span",{className:"text-[10px] font-medium uppercase tracking-wider text-zinc-400 dark:text-zinc-500",children:n}),a]})}const A=[{id:"fit",label:"Ajustar"},{id:.5,label:"50%"},{id:.75,label:"75%"},{id:1,label:"100%"}],E=[{id:"light",label:"Claro"},{id:"dark",label:"Oscuro"},{id:"split",label:"Ambos"}];function _({viewport:n,onViewport:a,zoom:o,onZoom:t,theme:i,onTheme:r,hint:m,actions:y,identity:x,backHref:u,zoomEnabled:S=!0,editing:p,onToggleEdit:z,pinCount:h,onOpenRequest:f,isolatedBase:g}){const k=w(),v=g?C(k,"",new URLSearchParams({...g,vp:n,zoom:String(o),theme:i,chrome:"off"})):null;return c("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-zinc-200 bg-white px-6 py-2 dark:border-zinc-700 dark:bg-zinc-900",children:[u&&e("a",{href:u,className:"rounded-md px-2 py-1 text-[11px] font-medium text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-100",children:"\u2190 Open-Flow"}),x&&e("span",{className:"max-w-[16rem] truncate rounded bg-zinc-100 px-2 py-1 font-mono text-[11px] text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",children:x}),e(b,{label:"Tama\xF1o",children:e(s,{value:n,onChange:a,options:[{id:j,label:"Auto"},...k.viewports.map(l=>({id:l.id,label:l.label,note:l.note}))]})}),e(b,{label:"Zoom",children:e(s,{value:o,onChange:t,options:A,disabled:!S,disabledReason:"En Auto el componente se dibuja en l\xEDnea, sin cuadro que escalar."})}),e(b,{label:"Tema",children:e(s,{value:i,onChange:r,options:E})}),y,c("span",{className:"ml-auto flex items-center gap-2",children:[z&&e("button",{type:"button",onClick:z,className:d("rounded-md px-2 py-1 text-[11px] font-medium transition-colors",p?"bg-amber-400 text-amber-950":"border border-zinc-300 text-zinc-600 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"),title:"Se\xF1alar elementos (E)",children:p?"Editando \u25CF":"Editar"}),f&&c("button",{type:"button",onClick:f,className:N,children:["Solicitud",(h??0)>0?` (${h})`:""]}),v&&e("a",{href:v,target:"_blank",rel:"noreferrer",className:N,title:"Sin cromo, en una pesta\xF1a nueva",children:"Abrir aislado \u2197"})]}),m&&e("p",{className:"basis-full text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:m})]})}export{j as AUTO,N as CHIP,_ as ScreenToolbar,s as SegmentedControl};
1
+ "use client";import{jsx as e,jsxs as c}from"react/jsx-runtime";import{useViewerConfig as C}from"./config-context";import{cx as o}from"./cx";import{workbenchUrl as j}from"./manifest";const N="rounded-md border border-zinc-300 px-2 py-1 text-[11px] font-medium text-zinc-600 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800",A="auto";function s({options:t,value:a,onChange:i,disabled:n=!1,disabledReason:d}){return e("div",{className:o("flex rounded-md border border-zinc-300 p-0.5 dark:border-zinc-700",n&&"opacity-40"),title:n?d:void 0,children:t.map(r=>e("button",{type:"button","aria-disabled":n||void 0,onClick:n?void 0:()=>i(r.id),title:n?d:r.note??void 0,className:o("rounded px-2 py-1 text-[11px] font-medium transition-colors",r.id===a?"bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:r.label},String(r.id)))})}function b({label:t,children:a}){return c("span",{className:"flex items-center gap-1.5",children:[e("span",{className:"text-[10px] font-medium uppercase tracking-wider text-zinc-400 dark:text-zinc-500",children:t}),a]})}const E=[{id:"fit",label:"Ajustar"},{id:.5,label:"50%"},{id:.75,label:"75%"},{id:1,label:"100%"}],F=[{id:"light",label:"Claro"},{id:"dark",label:"Oscuro"},{id:"split",label:"Ambos"}];function L({viewport:t,onViewport:a,zoom:i,onZoom:n,theme:d,onTheme:r,hint:u,zoomDisabledReason:y,actions:S,identity:x,backHref:m,zoomEnabled:w=!0,editing:p,onToggleEdit:z,pinCount:f,onOpenRequest:h,isolatedBase:g}){const k=C(),v=g?j(k,"",new URLSearchParams({...g,vp:t,...i===void 0?{}:{zoom:String(i)},theme:d,chrome:"off"})):null;return c("div",{className:"flex flex-wrap items-center gap-x-3 gap-y-2 border-b border-zinc-200 bg-white px-6 py-2 dark:border-zinc-700 dark:bg-zinc-900",children:[m&&e("a",{href:m,className:"rounded-md px-2 py-1 text-[11px] font-medium text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-100",children:"\u2190 Open-Flow"}),x&&e("span",{className:"max-w-[16rem] truncate rounded bg-zinc-100 px-2 py-1 font-mono text-[11px] text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",children:x}),e(b,{label:"Tama\xF1o",children:e(s,{value:t,onChange:a,options:[{id:A,label:"Auto"},...k.viewports.map(l=>({id:l.id,label:l.label,note:l.note}))]})}),e(b,{label:"Zoom",children:e(s,{value:i??"fit",onChange:n??(()=>{}),options:E,disabled:!w||n===void 0,disabledReason:y??"En Auto el componente se dibuja en l\xEDnea, sin cuadro que escalar."})}),e(b,{label:"Tema",children:e(s,{value:d,onChange:r,options:F})}),S,c("span",{className:"ml-auto flex items-center gap-2",children:[z&&e("button",{type:"button",onClick:z,className:o("rounded-md px-2 py-1 text-[11px] font-medium transition-colors",p?"bg-amber-400 text-amber-950":"border border-zinc-300 text-zinc-600 hover:bg-zinc-100 dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"),title:"Se\xF1alar elementos (E)",children:p?"Editando \u25CF":"Editar"}),h&&c("button",{type:"button",onClick:h,className:N,children:["Solicitud",(f??0)>0?` (${f})`:""]}),v&&e("a",{href:v,target:"_blank",rel:"noreferrer",className:N,title:"Sin cromo, en una pesta\xF1a nueva",children:"Abrir aislado \u2197"})]}),u&&e("p",{className:"basis-full text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:u})]})}export{A as AUTO,N as CHIP,L as ScreenToolbar,s as SegmentedControl};
package/dist/server.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { NewTicketInput, TicketSummary } from "./pin";
1
+ import type { NewTicketInput, SaveFlowInput, SaveFlowResult, TicketSummary } from "./pin";
2
2
  export declare function readTickets(): Promise<TicketSummary[]>;
3
3
  export declare function writeTicket(input: NewTicketInput): Promise<{
4
4
  ok: boolean;
@@ -7,3 +7,4 @@ export declare function writeTicket(input: NewTicketInput): Promise<{
7
7
  prompt?: string;
8
8
  error?: string;
9
9
  }>;
10
+ export declare function saveFlow(input: SaveFlowInput): Promise<SaveFlowResult>;
package/dist/server.js CHANGED
@@ -1 +1,2 @@
1
- import{execFile as k}from"node:child_process";import{promisify as y}from"node:util";const i=y(k),w=120,l=4e3,g=50,f=3e4,u=16*1024*1024;function m(){return!1}function _(){if(!m())throw new Error("El taller s\xF3lo escribe tickets en desarrollo.")}function r(t,n){return(t??"").slice(0,n).trim()}function d(){return"workbench"}function T(t){return typeof t=="object"&&t!==null&&"id"in t&&"path"in t}async function S(){if(!m())return[];try{const{stdout:t}=await i(d(),["tickets"],{cwd:process.cwd(),timeout:f,maxBuffer:u}),n=JSON.parse(t);return Array.isArray(n)?n:[]}catch{return[]}}async function X(t){_();const n=r(t.title,w);if(n.length===0)return{ok:!1,error:"Falta el t\xEDtulo."};const o=r(t.surface,100);if(o.length===0)return{ok:!1,error:"Falta la superficie."};const p=JSON.stringify({intent:r(t.intent,l),acceptance:r(t.acceptance,l),route:r(t.route,200),pins:(t.pins??[]).slice(0,g).map(e=>({note:r(e.note,500),element:{tag:r(e.element.tag,40),classes:r(e.element.classes,300),text:r(e.element.text,120),path:r(e.element.path,300)},node:e.node})),errors:(t.errors??[]).slice(0,20).map(e=>({message:r(e.message,500),component:e.component,file:e.file,route:e.route}))});try{const e=i(d(),["ticket",o,n,"--json"],{cwd:process.cwd(),timeout:f,maxBuffer:u});e.child.stdin?.on("error",()=>{}),e.child.stdin?.end(p);const{stdout:h}=await e,c=JSON.parse(h.trim());if(!T(c))return{ok:!0};const s=String(c.id),a=String(c.path).replace(`${process.cwd()}/`,""),E=`Lee ${a} y ejec\xFAtalo. Es un ticket del taller contra la superficie "${o}"; el mapa de archivos ya est\xE1 dentro. Al terminar: commit con ${s} en el mensaje, luego \`workbench validate ${s}\`.`;return{ok:!0,id:s,file:a,prompt:E}}catch(e){return{ok:!1,error:e instanceof Error?e.message:"Fall\xF3 la escritura."}}}export{S as readTickets,X as writeTicket};
1
+ import{execFile as _}from"node:child_process";import{promisify as $}from"node:util";const m=$(_),u=120,i=4e3,A=50,h=3e4,p=16*1024*1024;function y(){return process.env.NODE_ENV==="development"}function E(){if(!y())throw new Error("El taller s\xF3lo escribe en desarrollo.")}function s(e,r){return[...(e??"").trim()].slice(0,r).join("")}function g(){return"workbench"}function F(e){return typeof e=="object"&&e!==null&&"id"in e&&"path"in e}function k(e){if(typeof e=="object"&&e!==null&&"stderr"in e){const o=String(e.stderr).trim().split(`
2
+ `).filter(Boolean).at(-1);if(o)return o.replace(/^(error|Error):\s*/,"")}return e instanceof Error?e.message:"Fall\xF3 la escritura."}async function P(){if(!y())return[];try{const{stdout:e}=await m(g(),["tickets"],{cwd:process.cwd(),timeout:h,maxBuffer:p}),r=JSON.parse(e);return Array.isArray(r)?r:[]}catch{return[]}}async function J(e){return E(),b(()=>I(e))}async function I(e){const r=s(e.title,u);if(r.length===0)return{ok:!1,error:"Falta el t\xEDtulo."};const o=s(e.surface,100);if(o.length===0)return{ok:!1,error:"Falta la superficie."};const f=JSON.stringify({intent:s(e.intent,i),acceptance:s(e.acceptance,i),route:s(e.route,200),pins:(e.pins??[]).slice(0,A).map(n=>({note:s(n.note,500),element:{tag:s(n.element.tag,40),classes:s(n.element.classes,300),text:s(n.element.text,120),path:s(n.element.path,300)},node:n.node})),errors:(e.errors??[]).slice(0,20).map(n=>({message:s(n.message,500),component:n.component,file:n.file,route:n.route}))});try{const n=m(g(),["ticket",o,r,"--json"],{cwd:process.cwd(),timeout:h,maxBuffer:p});n.child.stdin?.on("error",()=>{}),n.child.stdin?.end(f);const{stdout:w}=await n,d=JSON.parse(w.trim());if(!F(d))return{ok:!0};const l=String(d.id),t=String(d.path),c=`Lee ${t} y ejec\xFAtalo. Es un ticket del taller contra la superficie "${o}"; el mapa de archivos ya est\xE1 dentro. Al terminar: commit con ${l} en el mensaje, luego \`workbench validate ${l}\`.`;return{ok:!0,id:l,file:t,prompt:c}}catch(n){return{ok:!1,error:k(n)}}}const S=12,T=80;function j(e){return e.length===0||e.length>100||e.startsWith("-")?!0:[...e].some(r=>{const o=r.codePointAt(0)??0;return o<32||o===127})}const v=[["route",200],["label",u],["via",T],["viewport",40],["note",2e3],["spec",i]];function B(e){for(const[r,o]of v)if((e[r]??"").length>o)return`${r} (l\xEDmite ${o})`;return null}function M(e){return typeof e=="object"&&e!==null&&"flowId"in e}let x=Promise.resolve();function b(e){const r=x.then(e,e);return x=r.catch(()=>{}),r}async function U(e){return E(),b(()=>X(e))}async function X(e){const r=(e.title??"").trim();if(e.id===null&&r.length===0)return{ok:!1,error:"Falta el t\xEDtulo."};if(r.length>u)return{ok:!1,error:`El t\xEDtulo es demasiado largo (l\xEDmite ${u}).`};if(e.id!==null&&j(e.id))return{ok:!1,error:"El id del recorrido no es v\xE1lido."};const o=e.steps??[],f=e.branches??[];if(!o.some(t=>(t.route??"").trim().length>0))return{ok:!1,error:"Un recorrido necesita al menos un paso con ruta."};for(const t of[...o,...f.flatMap(c=>c.steps??[])]){const c=B(t);if(c)return{ok:!1,error:`Un texto del paso \xAB${(t.route??"").slice(0,60)}\xBB es demasiado largo: ${c}.`}}for(const t of f)if((t.label??"").length>u||(t.from??"").length>200||(t.id??"").length>100)return{ok:!1,error:`La rama \xAB${(t.label||t.id||"").slice(0,60)}\xBB tiene un texto demasiado largo.`};if((e.description??"").length>i||(e.intent??"").length>i||(e.acceptance??"").length>i)return{ok:!1,error:`Un texto del recorrido es demasiado largo (l\xEDmite ${i}).`};if(o.length+f.reduce((t,c)=>t+(c.steps?.length??0),0)>S)return{ok:!1,error:`un recorrido tiene como m\xE1ximo ${S} pantallas, contando las ramas`};const w=JSON.stringify({title:r,description:e.description??"",intent:e.intent??"",acceptance:e.acceptance??"",steps:o,branches:f}),d=e.id===null?["flow","new",r,"--json"]:["flow","set",e.id,"--json"];let l;try{const t=m(g(),d,{cwd:process.cwd(),timeout:h,maxBuffer:p});t.child.stdin?.on("error",()=>{}),t.child.stdin?.end(w);const{stdout:c}=await t,a=JSON.parse(c.trim());if(!M(a))return{ok:!0};l={flowId:String(a.flowId),ticketId:a.ticketId==null?null:String(a.ticketId),file:a.path==null?null:String(a.path),created:"created"in a?!!a.created:!0}}catch(t){return{ok:!1,error:k(t)}}try{return await m(g(),["scan"],{cwd:process.cwd(),timeout:h,maxBuffer:p}),{ok:!0,...l}}catch(t){return{ok:!0,...l,note:`Se escribi\xF3, pero el scan fall\xF3: ${k(t)}. Ejecuta \`workbench scan\` para ver el cambio.`}}}export{P as readTickets,U as saveFlow,J as writeTicket};
@@ -1,5 +1,5 @@
1
1
  import type { Manifest } from "./manifest.types";
2
- import type { NewTicketInput, TicketSummary } from "./pin";
2
+ import type { NewTicketInput, SaveFlowInput, SaveFlowResult, TicketSummary } from "./pin";
3
3
  import type { RegistryEntry } from "./registry";
4
4
  import { type CreateResult } from "./ticket-drawer";
5
5
  export type Tab = "elementos" | "componentes" | "flujos" | "superficies";
@@ -13,10 +13,11 @@ export interface WorkbenchView {
13
13
  theme?: string;
14
14
  chrome?: string;
15
15
  }
16
- export declare function WorkbenchApp({ manifest, registry, tickets, view, onCreateTicket, }: {
16
+ export declare function WorkbenchApp({ manifest, registry, tickets, view, onCreateTicket, onSaveFlow, }: {
17
17
  manifest: Manifest;
18
18
  registry: RegistryEntry[];
19
19
  tickets: TicketSummary[];
20
20
  view: WorkbenchView;
21
21
  onCreateTicket: (input: NewTicketInput) => Promise<CreateResult>;
22
+ onSaveFlow?: (input: SaveFlowInput) => Promise<SaveFlowResult>;
22
23
  }): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as n,jsxs as o,Fragment as O}from"react/jsx-runtime";import{useMemo as x,useState as d}from"react";import{ManifestProvider as R}from"./config-context";import{cx as m}from"./cx";import{EditSessionOverlay as _,useEditSession as A}from"./edit-session";import{buildComponentIndex as D}from"./hover-inspect";import{byGroup as H,flowById as V,groupLabel as Z}from"./manifest";import{ComponentsPanel as q}from"./panels/components";import{ElementsPanel as G}from"./panels/elements";import{FlowsPanel as K}from"./panels/flows";import{SurfacesPanel as W}from"./panels/surfaces";import{TicketDrawer as $}from"./ticket-drawer";import{VerdictBadge as J}from"./component-preview";const w=[{id:"elementos",label:"Elementos"},{id:"componentes",label:"Componentes"},{id:"flujos",label:"Flujos"},{id:"superficies",label:"Superficies"}];function Q(e){return w.some(r=>r.id===e)&&e?e:"componentes"}function U(e){if(e==="fit")return"fit";const r=Number(e);return r===.5||r===.75||r===1?r:void 0}function X(e){return e==="light"||e==="dark"||e==="split"?e:void 0}function me({manifest:e,registry:r,tickets:N,view:i,onCreateTicket:C}){const[s,v]=d(Q(i.tab)),[F,j]=d(i.component??e.components[0]?.id),[E,I]=d(i.flow??e.flows[0]?.id),[S,P]=d(i.surface??e.surfaces[0]?.id),[h,T]=d(""),c=A({bareKeys:!0}),B=x(()=>D(e.components),[e.components]),g=i.chrome!=="off",u=e.components.find(t=>t.id===F)??e.components[0],l=V(e.flows,E),b=e.surfaces.find(t=>t.id===S)??e.surfaces[0],z={componentIndex:B,editing:c.editing,onPin:c.addPin,onHover:c.setHovering,onToggleEdit:c.toggleEditing,pinCount:c.pins.length,onOpenRequest:c.openDrawer,showChrome:g,initialViewport:i.vp,initialZoom:U(i.zoom),initialTheme:X(i.theme)},p=h.trim().toLowerCase(),k=x(()=>p?e.components.filter(t=>t.name.toLowerCase().includes(p)||t.file.toLowerCase().includes(p)):e.components,[e.components,p]),y=x(()=>H(k),[k]),L=s==="elementos"?n(G,{}):s==="flujos"?l?n(K,{flow:l,...z},l.id):n(f,{what:"recorridos",hint:"El rastreador no encontr\xF3 enlaces entre p\xE1ginas."}):s==="superficies"?b?n(W,{surface:b,tickets:N}):n(f,{what:"superficies",hint:"Nada bajo el directorio de la app importa una cabecera, un rail o una barra lateral."}):u?n(q,{entry:u,registry:r,...z}):n(f,{what:"componentes",hint:"Revisa `componentRoots` en workbench.config.json."});return o(R,{manifest:e,children:[o("div",{className:"fixed inset-0 flex bg-white text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100",children:[g&&o("nav",{className:"flex w-64 shrink-0 flex-col border-r border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900",children:[o("header",{className:"border-b border-zinc-200 px-4 py-3 dark:border-zinc-700",children:[n("h1",{className:"text-sm font-semibold",title:e.config.subtitle,children:"Open-Flow"}),n("p",{className:"mt-0.5 text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:e.config.title})]}),n("div",{className:"flex flex-wrap gap-1 border-b border-zinc-200 p-2 dark:border-zinc-700",children:w.map(t=>n("button",{type:"button",onClick:()=>v(t.id),className:m("rounded px-2 py-1 text-[11px] font-medium",t.id===s?"bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900":"text-zinc-600 hover:bg-zinc-200 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:t.label},t.id))}),o("div",{className:"min-h-0 flex-1 overflow-auto p-2",children:[s==="componentes"&&o(O,{children:[n("input",{value:h,onChange:t=>T(t.target.value),placeholder:`Filtrar ${e.components.length}\u2026`,className:"mb-2 w-full rounded border border-zinc-300 px-2 py-1 text-[11px] dark:border-zinc-700 dark:bg-zinc-800"}),y.map(([t,M])=>o("section",{className:"mb-3",children:[n("h2",{className:"px-1 pb-1 text-[10px] font-medium uppercase tracking-wider text-zinc-400",children:Z(e.config,t)}),M.map(a=>o("button",{type:"button",onClick:()=>j(a.id),className:m("flex w-full items-center gap-1.5 rounded px-2 py-1 text-left text-[12px]",a.id===u?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:[n("span",{className:"truncate",children:a.name}),a.verdict!=="auto"&&n("span",{className:"ml-auto shrink-0",children:n(J,{verdict:a.verdict})})]},a.id))]},t))]}),s==="flujos"&&e.flows.map(t=>n("button",{type:"button",onClick:()=>I(t.id),className:m("block w-full truncate rounded px-2 py-1 text-left text-[12px]",t.id===l?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:t.title},t.id)),s==="superficies"&&e.surfaces.map(t=>n("button",{type:"button",onClick:()=>P(t.id),className:m("block w-full truncate rounded px-2 py-1 text-left text-[12px]",t.id===b?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:t.label},t.id))]}),o("footer",{className:"border-t border-zinc-200 px-3 py-2 text-[10px] text-zinc-400 dark:border-zinc-700",children:[e.stats.filesScanned," archivos \xB7 ",e.stats.elapsedMs," ms \xB7"," ",e.generatedBy,e.stats.parseFailures.length>0&&o("span",{className:"block text-amber-600 dark:text-amber-400",children:[e.stats.parseFailures.length," archivos no se analizaron"]})]})]}),n("main",{className:"min-w-0 flex-1",children:L})]}),n(_,{session:c}),n($,{session:c,surfaces:e.surfaces.map(t=>({id:t.id,label:t.label,weight:t.weight,predictedFileCount:t.predictedFiles.length})),onCreate:C})]})}function f({what:e,hint:r}){return n("div",{className:"flex h-full items-center justify-center p-10",children:o("p",{className:"max-w-sm text-center text-sm text-zinc-500 dark:text-zinc-400",children:["No se encontraron ",e,". ",r]})})}export{me as WorkbenchApp};
1
+ "use client";import{jsx as n,jsxs as o,Fragment as q}from"react/jsx-runtime";import{useEffect as G,useMemo as p,useState as l}from"react";import{ManifestProvider as K}from"./config-context";import{cx as u}from"./cx";import{EditSessionOverlay as W,useEditSession as $}from"./edit-session";import{FlowStart as J}from"./flow-start";import{buildComponentIndex as Q}from"./hover-inspect";import{byGroup as U,flowById as X,groupLabel as Y,tokenCount as ee}from"./manifest";import{ComponentsPanel as te}from"./panels/components";import{ElementsPanel as ne}from"./panels/elements";import{FlowsPanel as oe}from"./panels/flows";import{SurfacesPanel as re}from"./panels/surfaces";import{TicketDrawer as ie}from"./ticket-drawer";import{VerdictBadge as ce}from"./component-preview";const E=[{id:"elementos",label:"Elementos"},{id:"componentes",label:"Componentes"},{id:"flujos",label:"Flujos"},{id:"superficies",label:"Superficies"}];function se(e){return E.find(r=>r.id===e)?.id??"componentes"}function le(e){if(e==="fit")return"fit";const r=Number(e);return r===.5||r===.75||r===1?r:void 0}function ae(e){return e==="light"||e==="dark"||e==="split"?e:void 0}function ve({manifest:e,registry:r,tickets:I,view:s,onCreateTicket:S,onSaveFlow:m}){const[i,B]=l(se(s.tab)),[P,T]=l(s.component??e.components[0]?.id),[R,w]=l(s.flow??e.flows[0]?.id),[L,A]=l(s.surface??e.surfaces[0]?.id),[N,D]=l(""),[M,b]=l(!1),[x,f]=l(null),c=$({bareKeys:!0}),C=p(()=>e.routes.filter(t=>t.kind==="page").map(t=>t.path),[e.routes]);G(()=>{x&&e.flows.some(t=>t.id===x)&&(b(!1),f(null))},[x,e.flows]);const O=p(()=>Q(e.components),[e.components]),v=s.chrome!=="off",g=e.components.find(t=>t.id===P)??e.components[0],a=X(e.flows,R),z=e.surfaces.find(t=>t.id===L)??e.surfaces[0],F={componentIndex:O,editing:c.editing,onPin:c.addPin,onHover:c.setHovering,onToggleEdit:c.toggleEditing,pinCount:c.pins.length,onOpenRequest:c.openDrawer,showChrome:v,initialViewport:s.vp,initialTheme:ae(s.theme)},Z=le(s.zoom),h=N.trim().toLowerCase(),j=p(()=>h?e.components.filter(t=>t.name.toLowerCase().includes(h)||t.file.toLowerCase().includes(h)):e.components,[e.components,h]),_=p(()=>U(j),[j]),y=p(()=>({elementos:ee(e.tokens),componentes:e.components.length,flujos:e.flows.length,superficies:e.surfaces.length}),[e]),H=i==="elementos"?n(ne,{}):i==="flujos"?m&&(M||!a)?n(J,{routes:C,waiting:x,onSave:m,onCreated:t=>{f(t),w(t)},onCancel:a?()=>b(!1):void 0,onReset:()=>f(null)}):a?n(oe,{flow:a,...F,routes:C,onSaveFlow:m},a.id):n(k,{what:"recorridos",hint:"El rastreador no encontr\xF3 enlaces entre p\xE1ginas."}):i==="superficies"?z?n(re,{surface:z,tickets:I}):n(k,{what:"superficies",hint:"Nada bajo el directorio de la app importa una cabecera, un rail o una barra lateral."}):g?n(te,{entry:g,registry:r,...F,initialZoom:Z}):n(k,{what:"componentes",hint:"Revisa `componentRoots` en workbench.config.json."});return o(K,{manifest:e,children:[o("div",{className:"fixed inset-0 flex bg-white text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100",children:[v&&o("nav",{className:"flex w-64 shrink-0 flex-col border-r border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-900",children:[o("header",{className:"border-b border-zinc-200 px-4 py-3 dark:border-zinc-700",children:[n("h1",{className:"text-sm font-semibold",title:e.config.subtitle,children:"Open-Flow"}),n("p",{className:"mt-0.5 text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:e.config.title})]}),n("ul",{className:"border-b border-zinc-200 p-2 dark:border-zinc-700",children:E.map(t=>n("li",{children:o("button",{type:"button",onClick:()=>B(t.id),"aria-current":t.id===i?"true":void 0,className:u("flex w-full items-center justify-between rounded px-2 py-1.5 text-left text-[12px] font-medium",t.id===i?"bg-zinc-900 text-white dark:bg-zinc-100 dark:text-zinc-900":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:[n("span",{children:t.label}),n("span",{className:u("text-[11px] tabular-nums",t.id===i?"text-zinc-300 dark:text-zinc-600":"text-zinc-500 dark:text-zinc-400"),children:y[t.id]})]})},t.id))}),o("div",{className:"min-h-0 flex-1 overflow-auto p-2",children:[i==="componentes"&&o(q,{children:[n("input",{value:N,onChange:t=>D(t.target.value),placeholder:`Filtrar ${e.components.length}\u2026`,className:"mb-2 w-full rounded border border-zinc-300 px-2 py-1 text-[11px] dark:border-zinc-700 dark:bg-zinc-800"}),_.map(([t,V])=>o("section",{className:"mb-3",children:[n("h2",{className:"px-1 pb-1 text-[10px] font-medium uppercase tracking-wider text-zinc-400",children:Y(e.config,t)}),V.map(d=>o("button",{type:"button",onClick:()=>T(d.id),className:u("flex w-full items-center gap-1.5 rounded px-2 py-1 text-left text-[12px]",d.id===g?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:[n("span",{className:"truncate",children:d.name}),d.verdict!=="auto"&&n("span",{className:"ml-auto shrink-0",children:n(ce,{verdict:d.verdict})})]},d.id))]},t))]}),i==="flujos"&&m&&n("button",{type:"button",onClick:()=>{c.closeDrawer(),b(!0)},className:"block w-full rounded px-2 py-1 text-left text-[12px] text-zinc-500 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-100",children:"+ A\xF1adir recorrido"}),i==="flujos"&&e.flows.map(t=>o("button",{type:"button",onClick:()=>{w(t.id),b(!1)},className:u("block w-full truncate rounded px-2 py-1 text-left text-[12px]",t.id===a?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:[t.title,t.branches.length>0&&o("span",{className:"ml-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:["\xB7 ",t.branches.length," ",t.branches.length===1?"rama":"ramas"]})]},t.id)),i==="superficies"&&e.surfaces.map(t=>n("button",{type:"button",onClick:()=>A(t.id),className:u("block w-full truncate rounded px-2 py-1 text-left text-[12px]",t.id===z?.id?"bg-zinc-200 font-medium dark:bg-zinc-800":"text-zinc-600 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800"),children:t.label},t.id))]}),o("footer",{className:"border-t border-zinc-200 px-3 py-2 text-[10px] text-zinc-400 dark:border-zinc-700",children:[e.stats.filesScanned," archivos \xB7 ",e.stats.elapsedMs," ms \xB7"," ",e.generatedBy,e.stats.parseFailures.length>0&&o("span",{className:"block text-amber-600 dark:text-amber-400",children:[e.stats.parseFailures.length," archivos no se analizaron"]})]})]}),n("main",{className:"min-w-0 flex-1",children:H})]}),n(W,{session:c}),n(ie,{session:c,surfaces:e.surfaces.map(t=>({id:t.id,label:t.label,weight:t.weight,predictedFileCount:t.predictedFiles.length})),onCreate:S})]})}function k({what:e,hint:r}){return n("div",{className:"flex h-full items-center justify-center p-10",children:o("p",{className:"max-w-sm text-center text-sm text-zinc-500 dark:text-zinc-400",children:["No se encontraron ",e,". ",r]})})}export{ve as WorkbenchApp};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-flow/ui",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "A live workbench over your Next.js app — real components, tokens, and flows. The open-flow viewer, open source.",
5
5
  "license": "Elastic-2.0",
6
6
  "repository": {