@open-flow/ui 0.4.0 → 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.
- package/dist/canvas-gestures.d.ts +15 -0
- package/dist/canvas-gestures.js +1 -0
- package/dist/connect-popover.d.ts +4 -19
- package/dist/connect-popover.js +1 -1
- package/dist/cta.d.ts +17 -0
- package/dist/cta.js +1 -0
- package/dist/floating.d.ts +35 -0
- package/dist/floating.js +1 -0
- package/dist/flow-canvas.d.ts +103 -0
- package/dist/flow-canvas.js +1 -0
- package/dist/flow-edit.d.ts +31 -0
- package/dist/flow-edit.js +1 -1
- package/dist/flow-frames.d.ts +2 -0
- package/dist/flow-graph.d.ts +109 -0
- package/dist/flow-graph.js +1 -0
- package/dist/flow-layout.d.ts +5 -10
- package/dist/flow-layout.js +1 -1
- package/dist/frame.d.ts +7 -2
- package/dist/frame.js +1 -1
- package/dist/journey-acts.d.ts +19 -0
- package/dist/journey-acts.js +1 -0
- package/dist/manifest.types.d.ts +3 -1
- package/dist/manifest.types.js +1 -1
- package/dist/mirror.d.ts +6 -1
- package/dist/mirror.js +1 -1
- package/dist/node-actions.d.ts +24 -0
- package/dist/node-actions.js +1 -0
- package/dist/node-menu.d.ts +13 -0
- package/dist/node-menu.js +1 -0
- package/dist/panels/flow-canvas-board.d.ts +49 -0
- package/dist/panels/flow-canvas-board.js +1 -0
- package/dist/panels/flow-cards.d.ts +9 -5
- package/dist/panels/flow-cards.js +1 -1
- package/dist/panels/flows.d.ts +1 -3
- package/dist/panels/flows.js +1 -1
- package/dist/pick-highlight.d.ts +6 -1
- package/dist/pick-highlight.js +1 -1
- package/dist/screen-frame.d.ts +2 -2
- package/dist/screen-frame.js +1 -1
- package/dist/screen-sketch.d.ts +30 -0
- package/dist/screen-sketch.js +1 -0
- package/dist/screen-toolbar.d.ts +4 -3
- package/dist/screen-toolbar.js +1 -1
- package/dist/workbench-app.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface CanvasGestures {
|
|
2
|
+
zoomAtClient: (clientX: number, clientY: number, deltaY: number, deltaMode: number) => void;
|
|
3
|
+
}
|
|
4
|
+
export declare const ZOOM_ATTR = "data-workbench-zoom";
|
|
5
|
+
export declare const CanvasGesturesContext: import("react").Context<CanvasGestures | null>;
|
|
6
|
+
export declare const ZOOM_VAR = "--wb-z";
|
|
7
|
+
export declare const NODE_WIDTH_VAR = "--wb-node-w";
|
|
8
|
+
export declare const CANVAS_BG_VAR = "--wb-canvas-bg";
|
|
9
|
+
export declare const EDGE_VAR = "--wb-edge";
|
|
10
|
+
export type CanvasDetail = "far" | "near";
|
|
11
|
+
export declare const DETAIL_IN = 0.55;
|
|
12
|
+
export declare const DETAIL_OUT = 0.45;
|
|
13
|
+
export declare function detailAt(z: number, previous: CanvasDetail): CanvasDetail;
|
|
14
|
+
export declare const CanvasDetailContext: import("react").Context<CanvasDetail>;
|
|
15
|
+
export declare const CANVAS_ATTR = "data-workbench-canvas";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{createContext as e}from"react";const c="data-workbench-zoom",s=e(null),x="--wb-z",A="--wb-node-w",p="--wb-canvas-bg",_="--wb-edge",n=.55,r=.45;function T(t,o){return t>=n?"near":t<=r?"far":o}const b=e("near"),i="data-workbench-canvas";export{i as CANVAS_ATTR,p as CANVAS_BG_VAR,b as CanvasDetailContext,s as CanvasGesturesContext,n as DETAIL_IN,r as DETAIL_OUT,_ as EDGE_VAR,A as NODE_WIDTH_VAR,c as ZOOM_ATTR,x as ZOOM_VAR,T as detailAt};
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
+
import type { ConnectAction, ConnectIntent } from "./flow-edit";
|
|
1
2
|
import type { MirrorPick } from "./frame";
|
|
2
|
-
export
|
|
3
|
-
export declare function placePopover(anchor: {
|
|
4
|
-
top: number;
|
|
5
|
-
left: number;
|
|
6
|
-
width: number;
|
|
7
|
-
height: number;
|
|
8
|
-
}, size: {
|
|
9
|
-
width: number;
|
|
10
|
-
height: number;
|
|
11
|
-
}, viewport: {
|
|
12
|
-
width: number;
|
|
13
|
-
height: number;
|
|
14
|
-
}): {
|
|
15
|
-
top: number;
|
|
16
|
-
left: number;
|
|
17
|
-
};
|
|
18
|
-
export declare function ConnectPopover({ pick, route, mode, onConfirm, onCancel, }: {
|
|
3
|
+
export declare function ConnectPopover({ pick, route, intent, onConfirm, onCancel, }: {
|
|
19
4
|
pick: MirrorPick;
|
|
20
5
|
route: string | null;
|
|
21
|
-
|
|
22
|
-
onConfirm: () => void;
|
|
6
|
+
intent: ConnectIntent;
|
|
7
|
+
onConfirm: (action: ConnectAction) => void;
|
|
23
8
|
onCancel: () => void;
|
|
24
9
|
}): import("react").JSX.Element;
|
package/dist/connect-popover.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsxs as
|
|
1
|
+
"use client";import{jsxs as t,jsx as m}from"react/jsx-runtime";import{useEffect as h,useRef as p}from"react";import{cx as b}from"./cx";import{useFloating as k}from"./floating";import{CHIP as y}from"./screen-toolbar";function j({pick:a,route:o,intent:r,onConfirm:c,onCancel:s}){const e=r.actions[0]??null,n=r.actions[1]??null,l=p(null),d=p(null),{ref:u,placed:i,style:x}=k(a.rect,s,r);return h(()=>{i&&(e?l.current:d.current)?.focus()},[i,e]),t("div",{ref:u,role:"dialog","aria-label":`Conectar \xAB${a.text||"control"}\xBB`,onKeyDown:f=>{f.key==="Escape"&&s()},className:"fixed z-[2147483050] w-72 rounded-md border border-zinc-200 bg-white p-2 shadow-lg dark:border-zinc-700 dark:bg-zinc-900",style:i?x:{...x,visibility:"hidden"},children:[t("p",{className:"truncate text-[12px] font-medium text-zinc-900 dark:text-zinc-100",children:["\xAB",a.text||"sin texto","\xBB",o&&t("span",{className:"ml-1 font-mono text-[11px] font-normal text-zinc-500 dark:text-zinc-400",children:["\u2192 ",o]})]}),t("p",{className:"mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400",children:[e?e.hint:"No se puede a\xF1adir desde aqu\xED.",!o&&e&&" \xB7 el control no lleva a una ruta de esta app, escr\xEDbela en la tarjeta"]}),r.existing&&t("p",{className:"mt-0.5 text-[11px] text-amber-700 dark:text-amber-300",children:["Esa pantalla ya est\xE1 en el recorrido (paso ",r.existing.number,")."]}),r.note&&m("p",{className:"mt-0.5 text-[11px] text-zinc-500 dark:text-zinc-400",children:r.note}),t("div",{className:"mt-2 flex items-center gap-2",children:[e&&t("button",{ref:l,type:"button",onClick:()=>c(e),className:"shrink-0 whitespace-nowrap rounded-md bg-zinc-900 px-2.5 py-1 text-[11px] font-medium text-white dark:bg-zinc-100 dark:text-zinc-900",children:["+ ",e.button]}),n&&t("button",{type:"button",onClick:()=>c(n),className:"shrink-0 whitespace-nowrap rounded px-1 text-[11px] font-medium text-sky-700 underline decoration-dotted underline-offset-2 hover:text-sky-900 dark:text-sky-300 dark:hover:text-sky-100",title:n.hint,children:["\xBF",n.verb,"?"]}),m("button",{ref:d,type:"button",onClick:s,className:b(y),children:"Cancelar"})]})]})}export{j as ConnectPopover};
|
package/dist/cta.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface CtaCandidate {
|
|
2
|
+
tag: string;
|
|
3
|
+
role: string | null;
|
|
4
|
+
type: string | null;
|
|
5
|
+
href: boolean;
|
|
6
|
+
onclick: boolean;
|
|
7
|
+
cursor: string;
|
|
8
|
+
name: string;
|
|
9
|
+
share: number;
|
|
10
|
+
disabled: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare const CTA_MAX_SHARE = 0.5;
|
|
13
|
+
export declare const CTA_MAX_DEPTH = 5;
|
|
14
|
+
export declare function isSemanticCta(node: CtaCandidate): boolean;
|
|
15
|
+
export declare function isStyledCta(node: CtaCandidate): boolean;
|
|
16
|
+
export declare function ctaOf(chain: readonly CtaCandidate[]): CtaCandidate | null;
|
|
17
|
+
export declare function semanticCtaOf(chain: readonly CtaCandidate[]): CtaCandidate | null;
|
package/dist/cta.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const i=new Set(["button","link","menuitem","menuitemcheckbox","tab","option"]),a=new Set(["BUTTON","SUMMARY","SELECT"]),s=new Set(["submit","button","reset","image"]),r=new Set(["HTML","BODY","MAIN","HEADER","FOOTER","NAV","SECTION","FORM"]),u=new Set(["LABEL","OPTION","INPUT","TEXTAREA","SELECT"]),f=.5,n=5;function l(t){return t.disabled||r.has(t.tag)?!1:t.tag==="A"?t.href:a.has(t.tag)?!0:t.tag==="INPUT"?t.type!==null&&s.has(t.type):t.role!==null&&i.has(t.role)?!0:t.onclick}function c(t){return t.disabled||r.has(t.tag)||u.has(t.tag)||t.cursor!=="pointer"||t.name.trim().length===0?!1:t.share<=f}function E(t){const e=t.slice(0,n);return T(e)??e.find(c)??null}function T(t){return t.slice(0,n).find(l)??null}export{n as CTA_MAX_DEPTH,f as CTA_MAX_SHARE,E as ctaOf,l as isSemanticCta,c as isStyledCta,T as semanticCtaOf};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare function placePopover(anchor: {
|
|
2
|
+
top: number;
|
|
3
|
+
left: number;
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
}, size: {
|
|
7
|
+
width: number;
|
|
8
|
+
height: number;
|
|
9
|
+
}, viewport: {
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}): {
|
|
13
|
+
top: number;
|
|
14
|
+
left: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function sameSpot(a: {
|
|
17
|
+
top: number;
|
|
18
|
+
left: number;
|
|
19
|
+
} | null, b: {
|
|
20
|
+
top: number;
|
|
21
|
+
left: number;
|
|
22
|
+
}): boolean;
|
|
23
|
+
export declare function useFloating(anchor: {
|
|
24
|
+
top: number;
|
|
25
|
+
left: number;
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
}, onDismiss: () => void, key?: unknown): {
|
|
29
|
+
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
30
|
+
placed: boolean;
|
|
31
|
+
style: {
|
|
32
|
+
top: number;
|
|
33
|
+
left: number;
|
|
34
|
+
};
|
|
35
|
+
};
|
package/dist/floating.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{useEffect as L,useLayoutEffect as y,useRef as w,useState as M}from"react";import{CANVAS_ATTR as b}from"./canvas-gestures";function A(e,t,c){let r=e.top+e.height+6;r+t.height>c.height-8&&(r=Math.max(8,e.top-t.height-6));const u=Math.min(Math.max(e.left,8),Math.max(8,c.width-t.width-8));return{top:r,left:u}}function S(e,t){return e!==null&&e.top===t.top&&e.left===t.left}function R(e,t,c){const{top:f,left:r,width:u,height:h}=e,s=w(null),g=w(!1),[v,x]=M(null),d=w(t);return L(()=>{d.current=t},[t]),y(()=>{const n=s.current;if(!n)return;const i=A({top:f,left:r,width:u,height:h},{width:n.offsetWidth,height:n.offsetHeight},{width:window.innerWidth,height:window.innerHeight});x(o=>S(o,i)?o:i)},[f,r,u,h,c]),L(()=>{const n=document.activeElement,i=a=>{const m=a.target;(!(m instanceof Node)||!s.current?.contains(m))&&d.current()},o=()=>d.current(),p=s.current,l=()=>{g.current=!0},E=()=>{g.current&&d.current()};return document.addEventListener("pointerdown",i,!0),window.addEventListener("blur",E),window.addEventListener("scroll",o,!0),window.addEventListener("resize",o),p?.addEventListener("focusin",l),window.addEventListener("focus",l),()=>{document.removeEventListener("pointerdown",i,!0),p?.removeEventListener("focusin",l),window.removeEventListener("focus",l),window.removeEventListener("blur",E),window.removeEventListener("scroll",o,!0),window.removeEventListener("resize",o);const a=document.activeElement;if(a!==null&&a!==document.body)return;((n instanceof HTMLElement&&n.isConnected&&n.tagName!=="IFRAME"&&n!==document.body?n:null)??document.querySelector(`[${b}]`))?.focus()}},[]),{ref:s,placed:v!==null,style:v??{top:0,left:0}}}export{A as placePopover,S as sameSpot,R as useFloating};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { type CanvasDetail } from "./canvas-gestures";
|
|
3
|
+
export interface CanvasViewport {
|
|
4
|
+
tx: number;
|
|
5
|
+
ty: number;
|
|
6
|
+
z: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CanvasNode {
|
|
9
|
+
key: string;
|
|
10
|
+
x: number;
|
|
11
|
+
y: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
element: ReactNode;
|
|
15
|
+
chrome?: ReactNode;
|
|
16
|
+
alwaysRender?: boolean;
|
|
17
|
+
behind?: boolean;
|
|
18
|
+
chromeLabel?: string;
|
|
19
|
+
onContextMenu?: (at: {
|
|
20
|
+
x: number;
|
|
21
|
+
y: number;
|
|
22
|
+
}) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface CanvasEdgeProp {
|
|
25
|
+
id: string;
|
|
26
|
+
from: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
to: {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
};
|
|
34
|
+
label: string | null;
|
|
35
|
+
variant: "solid" | "dashed";
|
|
36
|
+
waypoints?: ReadonlyArray<{
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
}>;
|
|
40
|
+
}
|
|
41
|
+
export interface CanvasBBox {
|
|
42
|
+
minX: number;
|
|
43
|
+
minY: number;
|
|
44
|
+
maxX: number;
|
|
45
|
+
maxY: number;
|
|
46
|
+
}
|
|
47
|
+
export declare const MIN_ZOOM = 0.1;
|
|
48
|
+
export declare const OPEN_ZOOM = 0.9;
|
|
49
|
+
export declare const MAX_ZOOM = 2;
|
|
50
|
+
export declare const FIT_PAD = 48;
|
|
51
|
+
export declare function zoomAt(viewport: CanvasViewport, cx: number, cy: number, k: number, min?: number, max?: number): CanvasViewport;
|
|
52
|
+
export declare function wheelZoomFactor(deltaY: number, deltaMode?: number): number;
|
|
53
|
+
export declare function fitViewport(host: {
|
|
54
|
+
width: number;
|
|
55
|
+
height: number;
|
|
56
|
+
}, bbox: CanvasBBox, pad?: number): CanvasViewport;
|
|
57
|
+
export declare function openViewport(host: {
|
|
58
|
+
width: number;
|
|
59
|
+
height: number;
|
|
60
|
+
}, bbox: CanvasBBox, first: {
|
|
61
|
+
width: number;
|
|
62
|
+
height: number;
|
|
63
|
+
} | null, pad?: number): CanvasViewport;
|
|
64
|
+
export declare function revealViewport(viewport: CanvasViewport, host: {
|
|
65
|
+
width: number;
|
|
66
|
+
height: number;
|
|
67
|
+
}, box: {
|
|
68
|
+
x: number;
|
|
69
|
+
y: number;
|
|
70
|
+
width: number;
|
|
71
|
+
height: number;
|
|
72
|
+
}, pad?: number): CanvasViewport;
|
|
73
|
+
interface Point {
|
|
74
|
+
x: number;
|
|
75
|
+
y: number;
|
|
76
|
+
}
|
|
77
|
+
export declare function edgePath(from: Point, to: Point, waypoints?: readonly Point[]): string;
|
|
78
|
+
export declare function edgeLabelPoint(from: Point, to: Point, waypoints?: readonly Point[]): {
|
|
79
|
+
x: number;
|
|
80
|
+
y: number;
|
|
81
|
+
};
|
|
82
|
+
export declare function FlowCanvas({ nodes, edges, bbox, fitKey, reveal, onViewportChange, onDetailChange, label, className, }: {
|
|
83
|
+
nodes: readonly CanvasNode[];
|
|
84
|
+
edges: readonly CanvasEdgeProp[];
|
|
85
|
+
bbox: CanvasBBox;
|
|
86
|
+
fitKey?: string;
|
|
87
|
+
reveal?: {
|
|
88
|
+
key: string;
|
|
89
|
+
tick: number;
|
|
90
|
+
} | null;
|
|
91
|
+
onViewportChange?: (viewport: CanvasViewport) => void;
|
|
92
|
+
onDetailChange?: (detail: CanvasDetail) => void;
|
|
93
|
+
label: string;
|
|
94
|
+
className?: string;
|
|
95
|
+
}): import("react").JSX.Element;
|
|
96
|
+
export declare function CanvasChrome({ anchor, clamp, className, style, children, }: {
|
|
97
|
+
anchor?: "top left" | "bottom left";
|
|
98
|
+
clamp?: boolean;
|
|
99
|
+
className?: string;
|
|
100
|
+
style?: React.CSSProperties;
|
|
101
|
+
children: ReactNode;
|
|
102
|
+
}): import("react").JSX.Element;
|
|
103
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as m,jsxs as M,Fragment as J}from"react/jsx-runtime";import{useEffect as $,useId as Q,useLayoutEffect as tt,useMemo as et,useRef as z,useState as B}from"react";import{CANVAS_BG_VAR as rt,CANVAS_ATTR as nt,CanvasDetailContext as it,DETAIL_IN as ot,CanvasGesturesContext as at,detailAt as ct,NODE_WIDTH_VAR as L,ZOOM_ATTR as st,ZOOM_VAR as P}from"./canvas-gestures";import{cx as T}from"./cx";const lt=.1,K=.9,ut=2,_=48,ht=(e,i,o)=>Math.min(o,Math.max(i,e));function v(e,i,o,c,l=lt,f=ut){const u=ht(e.z*c,Math.min(l,e.z),f);if(u===e.z)return e;const h=u/e.z;return{z:u,tx:i-(i-e.tx)*h,ty:o-(o-e.ty)*h}}function S(e,i=0){const o=i===1?e*16:i===2?e*160:e;return Math.exp(-o*.0015)}function j(e,i,o=_){const c=i.maxX-i.minX,l=i.maxY-i.minY;if(c<=0||l<=0)return{z:1,tx:e.width/2-i.minX,ty:e.height/2-i.minY};const f={width:Math.max(1,e.width-o*2),height:Math.max(1,e.height-o*2)},u=Math.min(f.width/c,f.height/l,1);return{z:u,tx:(e.width-c*u)/2-i.minX*u,ty:(e.height-l*u)/2-i.minY*u}}function dt(e,i,o,c=_){const l=j(e,i,c),f=Math.max(l.z,K),u=o?Math.min((e.width-c*2)/Math.max(1,o.width),(e.height-c*2)/Math.max(1,o.height)):f,h=Math.min(f,Math.max(l.z,ot,Math.min(K,u)));return h===l.z?l:{z:h,tx:c-i.minX*h,ty:c-i.minY*h}}function ft(e,i,o,c=_){const l=(x,g,w)=>{if(g+c*2>w||x<c)return c-x;const d=x+g;return d>w-c?w-c-d:0},f=e.tx+o.x*e.z,u=e.ty+o.y*e.z,h=l(f,o.width*e.z,i.width),b=l(u,o.height*e.z,i.height);return h===0&&b===0?e:{...e,tx:e.tx+h,ty:e.ty+b}}function xt(e,i,o){if(!o||o.length===0){const f=(e.x+i.x)/2;return`M ${e.x},${e.y} C ${f},${e.y} ${f},${i.y} ${i.x},${i.y}`}const c=[e,...o,i];let l=`M ${e.x},${e.y}`;for(let f=1;f<c.length;f+=1){const u=c[f-1],h=c[f];if(!u||!h)continue;const b=Math.abs(h.x-u.x)>=Math.abs(h.y-u.y),x=b?{x:(u.x+h.x)/2,y:u.y}:{x:u.x,y:(u.y+h.y)/2},g=b?{x:(u.x+h.x)/2,y:h.y}:{x:h.x,y:(u.y+h.y)/2};l+=` C ${x.x},${x.y} ${g.x},${g.y} ${h.x},${h.y}`}return l}function mt(e,i,o){if(o&&o.length>=2){const c=o[0],l=o[o.length-1];if(c&&l)return{x:(c.x+l.x)/2,y:(c.y+l.y)/2}}return{x:(e.x+i.x)/2,y:(e.y+i.y)/2}}const O=60;function zt({nodes:e,edges:i,bbox:o,fitKey:c,reveal:l,onViewportChange:f,onDetailChange:u,label:h,className:b}){const x=z(null),g=z(null),w=z(null),[d,p]=B(null),[C,Z]=B("near"),y=z(!1),A=z(null),F=Q(),E=(t="fit")=>{const r=x.current;if(!r)return;const n=r.getBoundingClientRect();if(n.width===0||n.height===0)return;const a={width:n.width,height:n.height},s=e.find(N=>!N.behind&&N.width>1)??null;p(t==="open"?dt(a,o,s&&{width:s.width,height:s.height}):j(a,o))},k=z(E);k.current=E,tt(()=>{k.current("open")},[]);const I=z(!0);$(()=>{if(I.current){I.current=!1;return}y.current=!1,k.current("open")},[c]),$(()=>{const t=x.current;if(!t||typeof ResizeObserver>"u")return;const r=new ResizeObserver(()=>{y.current||k.current("open")});return r.observe(t),()=>r.disconnect()},[]),$(()=>{u?.(C)},[C]),$(()=>{if(!l)return;const t=e.find(a=>a.key===l.key),r=x.current;if(!t||!r)return;const n=r.getBoundingClientRect();p(a=>a&&ft(a,{width:n.width,height:n.height},t))},[l?.tick]),$(()=>{d&&(f?.(d),Z(t=>{const r=ct(d.z,t);if(r==="far"&&t==="near"){const n=x.current,a=n?.ownerDocument.activeElement;n&&a instanceof HTMLElement&&n.contains(a)&&a!==n&&n.focus()}return r}))},[d]),$(()=>{const t=x.current;if(!t)return;const r=n=>{if(!n.ctrlKey&&!n.metaKey){const s=n.target;if(s instanceof Element&&s.closest("textarea, input, select, [contenteditable='true']"))return}n.preventDefault(),y.current=!0;const a=t.getBoundingClientRect();p(s=>s&&(n.ctrlKey||n.metaKey?v(s,n.clientX-a.left,n.clientY-a.top,S(n.deltaY,n.deltaMode)):{...s,tx:s.tx-n.deltaX,ty:s.ty-n.deltaY}))};return t.addEventListener("wheel",r,{passive:!1}),()=>t.removeEventListener("wheel",r)},[]);const R=t=>{y.current=!0;const r=x.current;if(!r)return;const n=r.getBoundingClientRect();p(a=>a&&v(a,n.width/2,n.height/2,t))},W=t=>{y.current=!0;const r=x.current;if(!r)return;const n=r.getBoundingClientRect();p(a=>a&&v(a,n.width/2,n.height/2,t/a.z))},H=t=>{if(t.button===0&&!(t.target!==x.current&&t.target!==g.current&&t.target!==w.current)){A.current={id:t.pointerId,x:t.clientX,y:t.clientY,moved:!1};try{t.currentTarget.setPointerCapture(t.pointerId)}catch{}}},G=t=>{const r=A.current;if(!r||r.id!==t.pointerId)return;if(t.buttons===0){A.current=null;return}const n=t.clientX-r.x,a=t.clientY-r.y;!r.moved&&Math.hypot(n,a)<4||(r.moved=!0,y.current=!0,r.x=t.clientX,r.y=t.clientY,p(s=>s&&{...s,tx:s.tx+n,ty:s.ty+a}))},X=t=>{A.current?.id===t.pointerId&&(A.current=null)},U=et(()=>({zoomAtClient:(t,r,n,a)=>{const s=x.current;if(!s)return;y.current=!0;const N=s.getBoundingClientRect();p(D=>D&&v(D,t-N.left,r-N.top,S(n,a)))}}),[]),q=t=>{if(t.target!==x.current)return;const r=(n,a)=>{y.current=!0,p(s=>s&&{...s,tx:s.tx+n,ty:s.ty+a})};switch(t.key){case"ArrowLeft":r(O,0);break;case"ArrowRight":r(-O,0);break;case"ArrowUp":r(0,O);break;case"ArrowDown":r(0,-O);break;case"+":case"=":R(1.2);break;case"-":R(1/1.2);break;case"0":y.current=!1,k.current();break;default:return}t.preventDefault()},Y=d?.z??1,V=`wb-arrow-${F}`;return m(at.Provider,{value:U,children:m(it.Provider,{value:C,children:m("div",{ref:x,role:"region","aria-label":h,"aria-roledescription":"lienzo",[nt]:"",tabIndex:0,onPointerDown:H,onPointerMove:G,onPointerUp:X,onPointerCancel:X,onKeyDown:q,className:T("relative h-full w-full cursor-grab touch-none select-none [overflow:clip] active:cursor-grabbing","focus-visible:outline-2 focus-visible:outline-sky-400",b),children:d&&M(J,{children:[m("div",{ref:g,"aria-hidden":!0,className:"absolute inset-0 text-zinc-950/[0.12] transition-opacity duration-200 dark:text-white/10",style:{backgroundImage:"radial-gradient(currentColor 1px, transparent 1px)",backgroundSize:"32px 32px",backgroundPosition:`${d.tx}px ${d.ty}px`,opacity:d.z<.5?0:1}}),M("div",{ref:w,[st]:String(d.z),className:"absolute left-0 top-0 will-change-transform",style:{transform:`translate(${d.tx}px, ${d.ty}px) scale(${d.z})`,transformOrigin:"0 0",[P]:String(d.z)},children:[e.filter(t=>t.behind).map(t=>m("div",{"aria-hidden":!0,className:"pointer-events-none absolute",style:{left:t.x,top:t.y,width:t.width,height:t.height},children:t.element},t.key)),M("svg",{"aria-hidden":!0,width:"1",height:"1",className:"pointer-events-none absolute left-0 top-0 text-zinc-400 dark:text-zinc-500",style:{overflow:"visible"},children:[m("defs",{children:m("marker",{id:V,viewBox:"0 0 10 10",refX:"8.5",refY:"5",markerWidth:"7",markerHeight:"7",orient:"auto-start-reverse",children:m("path",{d:"M 0 0 L 10 5 L 0 10 z",fill:"currentColor"})})}),i.map(t=>{const r=mt(t.from,t.to,t.waypoints);return M("g",{children:[m("path",{d:xt(t.from,t.to,t.waypoints),fill:"none",stroke:"currentColor",strokeWidth:1.75/d.z,strokeDasharray:t.variant==="dashed"?`${8/d.z} ${6/d.z}`:void 0,markerEnd:`url(#${V})`}),t.label&&C==="near"&&m("text",{transform:`translate(${r.x} ${r.y}) scale(${1/d.z})`,textAnchor:"middle",dominantBaseline:"middle",fontSize:12,stroke:`var(${rt}, #fafafa)`,strokeWidth:5,style:{paintOrder:"stroke"},className:"fill-zinc-600 font-medium dark:fill-zinc-300",children:t.label})]},t.id)})]}),e.filter(t=>!t.behind).map(t=>m("div",{className:"absolute select-text",style:{left:t.x,top:t.y,width:t.width,...t.alwaysRender?{}:{contentVisibility:"auto",containIntrinsicSize:`${t.width}px ${t.height}px`}},onContextMenu:t.onContextMenu?r=>{r.preventDefault(),t.onContextMenu?.({x:r.clientX,y:r.clientY})}:void 0,children:t.element},t.key)),e.filter(t=>t.chrome).map(t=>m("div",{className:"pointer-events-none absolute",role:"group","aria-label":t.chromeLabel,style:{left:t.x,top:t.y,width:t.width,height:t.height,[L]:String(t.width)},children:t.chrome},`chrome:${t.key}`))]}),M("div",{className:"absolute bottom-3 left-3 z-10 flex items-center gap-1 rounded-md border border-zinc-950/10 bg-white/70 p-1 text-zinc-600 shadow-sm backdrop-blur transition-opacity duration-200 hover:bg-white/95 dark:border-white/10 dark:bg-zinc-900/70 dark:text-zinc-300 dark:hover:bg-zinc-900/95",children:[C==="far"&&m("span",{role:"status",className:"px-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:"Vista de mapa \xB7 ac\xE9rcate para ver las p\xE1ginas y editarlas"}),m("button",{type:"button","aria-label":"Ajustar el recorrido a la vista",onClick:()=>{y.current=!1,k.current()},className:"rounded px-2 py-1 text-[11px] font-medium hover:bg-zinc-950/5 dark:hover:bg-white/10",children:"Ajustar"}),m("button",{type:"button","aria-label":"Alejar",onClick:()=>R(1/1.2),className:"rounded px-2 py-1 text-[13px] leading-none hover:bg-zinc-950/5 dark:hover:bg-white/10",children:"\u2212"}),M("button",{type:"button","aria-label":`${Math.round(Y*100)}% \u2014 volver al 100%`,onClick:()=>W(1),className:"rounded px-1.5 py-1 font-mono text-[11px] tabular-nums hover:bg-zinc-950/5 dark:hover:bg-white/10",children:[Math.round(Y*100),"%"]}),m("button",{type:"button","aria-label":"Acercar",onClick:()=>R(1.2),className:"rounded px-2 py-1 text-[13px] leading-none hover:bg-zinc-950/5 dark:hover:bg-white/10",children:"+"})]})]})})})})}function wt({anchor:e="top left",clamp:i=!1,className:o,style:c,children:l}){return m("div",{className:T("pointer-events-auto absolute w-max select-text",o),style:{transform:`scale(calc(1 / var(${P}, 1)))`,transformOrigin:e,...i?{maxWidth:`max(9rem, calc(var(${L}, 9999) * var(${P}, 1) * 1px))`}:{},...c},children:l})}export{wt as CanvasChrome,_ as FIT_PAD,zt as FlowCanvas,ut as MAX_ZOOM,lt as MIN_ZOOM,K as OPEN_ZOOM,mt as edgeLabelPoint,xt as edgePath,j as fitViewport,dt as openViewport,ft as revealViewport,S as wheelZoomFactor,v as zoomAt};
|
package/dist/flow-edit.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type Lane } from "./flow-layout";
|
|
2
|
+
import { type JourneyAct } from "./journey-acts";
|
|
1
3
|
import type { Flow } from "./manifest.types";
|
|
2
4
|
import type { FlowBody, FlowStepInput } from "./pin";
|
|
3
5
|
export type LaneRef = {
|
|
@@ -7,6 +9,8 @@ export type LaneRef = {
|
|
|
7
9
|
index: number;
|
|
8
10
|
id: string;
|
|
9
11
|
};
|
|
12
|
+
export declare function laneRefOf(lane: Lane): LaneRef;
|
|
13
|
+
export declare function refKey(ref: LaneRef): string;
|
|
10
14
|
export interface ComposerTarget {
|
|
11
15
|
lane: LaneRef;
|
|
12
16
|
after: number;
|
|
@@ -18,8 +22,35 @@ export declare function bodyOf(flow: Flow, defaultViewport?: string): FlowBody;
|
|
|
18
22
|
export declare function appendStep(body: FlowBody, lane: LaneRef, step: FlowStepInput): FlowBody;
|
|
19
23
|
export declare function forkFrom(body: FlowBody, fromRoute: string, branchLabel: string, step: FlowStepInput): FlowBody;
|
|
20
24
|
export declare function removeLast(body: FlowBody, lane: LaneRef): FlowBody;
|
|
25
|
+
export declare function stepCount(flow: Flow): number;
|
|
26
|
+
export declare function atCap(flow: Flow, maxFlowSteps: number): boolean;
|
|
21
27
|
export declare function isForkPoint(flow: Flow, position: number): boolean;
|
|
22
28
|
export declare function forkResolvesTo(flow: Flow, route: string): number;
|
|
29
|
+
export type ConnectAction = JourneyAct;
|
|
30
|
+
export interface PickedStep {
|
|
31
|
+
lane: "trunk" | "branch";
|
|
32
|
+
position: number;
|
|
33
|
+
isLast: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface ConnectIntent {
|
|
36
|
+
actions: ConnectAction[];
|
|
37
|
+
note: string | null;
|
|
38
|
+
existing: {
|
|
39
|
+
number: number;
|
|
40
|
+
label: string;
|
|
41
|
+
} | null;
|
|
42
|
+
}
|
|
43
|
+
export declare function journeyMoves(at: {
|
|
44
|
+
lane: "trunk" | "branch";
|
|
45
|
+
isLast: boolean;
|
|
46
|
+
}, atCap?: boolean): {
|
|
47
|
+
append: boolean;
|
|
48
|
+
fork: boolean;
|
|
49
|
+
why: string | null;
|
|
50
|
+
};
|
|
51
|
+
export declare function connectIntent(flow: Flow, at: PickedStep, route: string | null, atCap?: boolean): ConnectIntent;
|
|
52
|
+
export declare function connectGestures(intent: ConnectIntent): string;
|
|
53
|
+
export declare function actionFor(intent: ConnectIntent, wanted: ConnectAction["kind"]): ConnectAction | null;
|
|
23
54
|
export declare function routeOfHref(href: string | null, origin: string): string | null;
|
|
24
55
|
export declare function hasStepAt(flow: Flow, at: ComposerTarget, route: string): boolean;
|
|
25
56
|
export declare function stepInputOf(draft: {
|
package/dist/flow-edit.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const i=e=>e??"";function
|
|
1
|
+
import{laneKey as l}from"./flow-layout";import{APPEND as d,AT_CAP as k,CLICK as m,FORK as x,NOWHERE as h,ONLY_AT_THE_END as p,ONLY_FROM_TRUNK as c,RIGHT_CLICK as v}from"./journey-acts";function _(e){return e.kind==="trunk"?{kind:"trunk"}:{kind:"branch",index:e.index,id:e.id}}function y(e){return l(e.kind==="trunk"?null:e.id)}const i=e=>e??"";function b(e,t){return{route:e.route,label:e.label,via:i(e.via),viewport:e.viewport===t?"":e.viewport,note:i(e.note),spec:i(e.spec)}}function K(e,t=""){const n=r=>b(r,t);return{title:e.title,description:i(e.description),intent:"",acceptance:"",steps:e.steps.map(n),branches:e.branches.map(r=>({id:r.id,label:r.label,from:e.steps[r.from]?.route??"",steps:r.steps.map(n)}))}}function N(e,t,n){return t.kind==="trunk"?{...e,steps:[...e.steps,n]}:{...e,branches:e.branches.map((r,s)=>s===t.index?{...r,steps:[...r.steps,n]}:r)}}function T(e,t,n,r){return{...e,branches:[...e.branches,{label:n,from:t,steps:[r]}]}}function $(e,t){if(t.kind==="trunk")return{...e,steps:e.steps.slice(0,-1)};const n=e.branches.map((r,s)=>s===t.index?{...r,steps:r.steps.slice(0,-1)}:r);return{...e,branches:n.filter(r=>r.steps.length>0)}}function O(e){return e.steps.length+e.branches.reduce((t,n)=>t+n.steps.length,0)}function A(e,t){return O(e)>=t}function E(e,t){return e.branches.some(n=>n.from===t)}function L(e,t){return e.steps.findIndex(n=>n.route===t)}function R(e,t=!1){return t?{append:!1,fork:!1,why:k}:e.lane==="trunk"?e.isLast?{append:!0,fork:!0,why:null}:{append:!1,fork:!0,why:p}:e.isLast?{append:!0,fork:!1,why:c}:{append:!1,fork:!1,why:`${p} ${c}`}}function F(e,t,n,r=!1){const s=R(t,r),o=[];s.append&&o.push(d),s.fork&&o.push(x);const u=n===null?-1:e.steps.findIndex(a=>a.route===n),f=u===-1?null:{number:u+1,label:e.steps[u]?.label??n??""};return{actions:o,note:s.why,existing:f}}function g(e){const t=e.actions[0];if(!t)return h;const n=[`${m} ${t.verb}`],r=e.actions.find(s=>s.kind==="fork");return r&&r!==t&&n.push(`${v} ${r.verb}`),n.join(" \xB7 ")}function H(e,t){return e.actions.find(n=>n.kind===t)??e.actions[0]??null}function P(e,t){if(!e)return null;try{const n=new URL(e,t);return n.origin===t?n.pathname:null}catch{return null}}function j(e,t,n){if(t.fork){const r=L(e,e.steps[t.after]?.route??"");return e.branches.some(s=>s.from===r&&s.steps[0]?.route===n)}return t.lane.kind==="trunk"?e.steps[t.after+1]?.route===n:e.branches[t.lane.index]?.steps[t.after+1]?.route===n}function D(e,t){const n={route:e.route.trim()};t&&(n.viewport=t);const r=e.label.trim();r&&(n.label=r);const s=e.via.trim();s&&(n.via=s);const o=e.spec.trim();return o&&(n.spec=o),n}export{H as actionFor,N as appendStep,A as atCap,K as bodyOf,g as connectGestures,F as connectIntent,T as forkFrom,L as forkResolvesTo,j as hasStepAt,E as isForkPoint,R as journeyMoves,_ as laneRefOf,y as refKey,$ as removeLast,P as routeOfHref,O as stepCount,D as stepInputOf};
|
package/dist/flow-frames.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { StepNode } from "./flow-layout";
|
|
2
|
+
import type { Sketch } from "./screen-sketch";
|
|
2
3
|
export type StepFrame = {
|
|
3
4
|
kind: "queued";
|
|
4
5
|
} | {
|
|
@@ -7,6 +8,7 @@ export type StepFrame = {
|
|
|
7
8
|
} | {
|
|
8
9
|
kind: "mirrored";
|
|
9
10
|
srcdoc: string;
|
|
11
|
+
sketch: Sketch | null;
|
|
10
12
|
capturedAt: number;
|
|
11
13
|
} | {
|
|
12
14
|
kind: "restless";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type Lane, type StepNode } from "./flow-layout";
|
|
2
|
+
export interface Box {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
export type GraphNode = {
|
|
7
|
+
kind: "step";
|
|
8
|
+
key: string;
|
|
9
|
+
node: StepNode;
|
|
10
|
+
box: Box;
|
|
11
|
+
anchorY: number;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "card";
|
|
14
|
+
key: string;
|
|
15
|
+
laneKey: string;
|
|
16
|
+
fork: boolean;
|
|
17
|
+
box: Box;
|
|
18
|
+
anchorY: number;
|
|
19
|
+
};
|
|
20
|
+
export interface GraphEdge {
|
|
21
|
+
id: string;
|
|
22
|
+
from: string;
|
|
23
|
+
to: string;
|
|
24
|
+
label: string | null;
|
|
25
|
+
kind: "step" | "fork" | "tail";
|
|
26
|
+
}
|
|
27
|
+
export interface GraphBand {
|
|
28
|
+
laneKey: string;
|
|
29
|
+
label: string | null;
|
|
30
|
+
forkKey: string | null;
|
|
31
|
+
orphan: boolean;
|
|
32
|
+
nodeKeys: string[];
|
|
33
|
+
}
|
|
34
|
+
export interface FlowGraph {
|
|
35
|
+
nodes: GraphNode[];
|
|
36
|
+
edges: GraphEdge[];
|
|
37
|
+
bands: GraphBand[];
|
|
38
|
+
}
|
|
39
|
+
export declare const NODE_GAP_X = 140;
|
|
40
|
+
export declare const NODE_GAP_Y = 120;
|
|
41
|
+
export declare const SECTION_LIFT = 120;
|
|
42
|
+
export declare const SECTION_PAD = 28;
|
|
43
|
+
export declare const TARGET_ASPECT = 1.6;
|
|
44
|
+
export declare const FORK_CARD_BAND = "card:fork";
|
|
45
|
+
export declare function tailKeyOf(laneKey: string): string;
|
|
46
|
+
export declare function forkCardKey(flat: number): string;
|
|
47
|
+
export declare function labelGap(label: string | null): number;
|
|
48
|
+
export declare function cardBoxOf(viewport: {
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
}): Box;
|
|
52
|
+
export interface TailSpec {
|
|
53
|
+
tail: ReadonlyMap<string, Box>;
|
|
54
|
+
forkCard: {
|
|
55
|
+
fork: number;
|
|
56
|
+
width: number;
|
|
57
|
+
height: number;
|
|
58
|
+
label: string;
|
|
59
|
+
} | null;
|
|
60
|
+
}
|
|
61
|
+
export declare function graphOf(lanes: readonly Lane[], tails?: TailSpec): FlowGraph;
|
|
62
|
+
export interface BandPlacement {
|
|
63
|
+
laneKey: string;
|
|
64
|
+
label: string | null;
|
|
65
|
+
row: number;
|
|
66
|
+
x: number;
|
|
67
|
+
y: number;
|
|
68
|
+
width: number;
|
|
69
|
+
height: number;
|
|
70
|
+
section: Box & {
|
|
71
|
+
x: number;
|
|
72
|
+
y: number;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export declare function layoutGraph(graph: FlowGraph): {
|
|
76
|
+
positions: ReadonlyMap<string, {
|
|
77
|
+
x: number;
|
|
78
|
+
y: number;
|
|
79
|
+
}>;
|
|
80
|
+
bands: BandPlacement[];
|
|
81
|
+
bbox: {
|
|
82
|
+
minX: number;
|
|
83
|
+
minY: number;
|
|
84
|
+
maxX: number;
|
|
85
|
+
maxY: number;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export interface ProjectedEdge {
|
|
89
|
+
id: string;
|
|
90
|
+
from: {
|
|
91
|
+
x: number;
|
|
92
|
+
y: number;
|
|
93
|
+
};
|
|
94
|
+
to: {
|
|
95
|
+
x: number;
|
|
96
|
+
y: number;
|
|
97
|
+
};
|
|
98
|
+
label: string | null;
|
|
99
|
+
variant: "solid" | "dashed";
|
|
100
|
+
waypoints?: Array<{
|
|
101
|
+
x: number;
|
|
102
|
+
y: number;
|
|
103
|
+
}>;
|
|
104
|
+
}
|
|
105
|
+
export declare function gutterBetween(sourceBottom: number, targetTop: number): number;
|
|
106
|
+
export declare function projectEdges(graph: FlowGraph, positions: ReadonlyMap<string, {
|
|
107
|
+
x: number;
|
|
108
|
+
y: number;
|
|
109
|
+
}>): ProjectedEdge[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{laneKey as R}from"./flow-layout";import{FRAME_CHROME as T}from"./screen-frame";const $=140,X=120,H=15,j=120,A=28,S=1.6,D="card:fork";function F(o){return`card:${o}`}function z(o){return`card:fork:${o}`}function C(o){return o?Math.max($,o.length*H+40):$}function W(o){return{width:o.width+T,height:o.height+T}}function q(o){const h=W(o.viewport);return{box:h,anchorY:h.height/2}}function U(o,h){const d=[],c=[],r=[],x=o.find(n=>n.kind==="trunk");for(const n of o){const a=n.kind==="trunk"?R(null):R(n.id),i=[];for(const[e,t]of n.nodes.entries()){const{box:u,anchorY:b}=q(t);d.push({kind:"step",key:t.key,node:t,box:u,anchorY:b}),i.push(t.key);const m=n.nodes[e-1];m&&c.push({id:`${m.key}\u2192${t.key}`,from:m.key,to:t.key,label:t.step.via?`\xAB${t.step.via}\xBB`:null,kind:"step"})}let l=null,p=!1;if(n.kind==="branch"){const e=n.fork===null?void 0:x?.nodes[n.fork],t=n.nodes[0];e&&t?(l=e.key,c.push({id:`${e.key}\u2192${t.key}`,from:e.key,to:t.key,label:n.label,kind:"fork"})):p=n.fork===null}const k=h?.tail.get(a),g=i[i.length-1];if(k){const e=F(a);d.push({kind:"card",key:e,laneKey:a,fork:!1,box:k,anchorY:k.height/2}),i.push(e),g&&c.push({id:`${g}\u2192${e}`,from:g,to:e,label:null,kind:"tail"})}r.push({laneKey:a,label:n.kind==="trunk"?null:`${n.label} \xB7 desde el paso ${n.from+1}`+(n.nodes[0]?.step.via?` \xB7 v\xEDa \xAB${n.nodes[0].step.via}\xBB`:"")+(n.fork===null?" \u2014 ese paso no est\xE1 en el tronco":""),forkKey:l,orphan:p,nodeKeys:i})}const s=h?.forkCard;if(s){const n=x?.nodes.find(l=>l.flat===s.fork),a=z(s.fork),i={width:s.width,height:s.height};d.push({kind:"card",key:a,laneKey:D,fork:!0,box:i,anchorY:i.height/2}),n&&c.push({id:`${n.key}\u2192${a}`,from:n.key,to:a,label:s.label,kind:"fork"}),r.push({laneKey:D,label:`${s.label} \xB7 desde el paso ${s.fork+1}`,forkKey:n?.key??null,orphan:n===void 0,nodeKeys:[a]})}return{nodes:d,edges:c,bands:r}}function V(o){const h=new Map(o.nodes.map(e=>[e.key,e])),d=new Map,c=new Map;for(const e of o.edges)(e.kind==="step"||e.kind==="fork")&&c.set(e.to,e.label);const r=e=>{let t=0,u=0;for(const[b,m]of e.nodeKeys.entries()){const y=h.get(m);if(!y)continue;t+=y.box.width,u=Math.max(u,y.box.height);const O=e.nodeKeys[b+1];O&&(t+=C(c.get(O)??null))}return{width:t,height:u}},x=new Map;for(const e of o.bands)for(const t of e.nodeKeys)x.set(t,e.laneKey);const s=[],n=new Map,a=[];let i=0;for(const e of o.bands){const t=r(e);let u=0,b=null,m=0;if(e.forkKey){const w=d.get(e.forkKey),K=h.get(e.forkKey),M=e.nodeKeys[0];w&&K&&(u=w.x+K.box.width+C(M?c.get(M)??null:null)),b=x.get(e.forkKey)??null,m=(b!==null?n.get(b):void 0)??0}const y=s[s.length-1],O=y!==void 0&&!(b!==null&&y.lanes.has(b))&&!e.orphan&&s.length-1>=m;let I=O;if(O&&y){const w=s.reduce((_,E)=>Math.max(_,E.right),0),K=Math.max(w,Math.max(u,y.right+$)+t.width),M=Math.max(i,y.y+Math.max(y.height,t.height)),Y=Math.max(w,u+t.width),B=i+X+t.height,L=(_,E)=>Math.abs(_/Math.max(1,E)-S);I=L(K,M)<=L(Y,B)}const f=I&&y?y:{y:s.length===0?0:i+X,right:0,height:0,lanes:new Set};f!==y&&s.push(f);const v=s.length-1,N=f.lanes.size===0?u:Math.max(u,f.right+$);let G=N;for(const[w,K]of e.nodeKeys.entries()){const M=h.get(K);if(!M)continue;d.set(K,{x:G,y:f.y});const Y=e.nodeKeys[w+1];G+=M.box.width+(Y?C(c.get(Y)??null):0)}f.right=Math.max(f.right,N+t.width),f.height=Math.max(f.height,t.height),f.lanes.add(e.laneKey),n.set(e.laneKey,v),i=Math.max(i,f.y+f.height),a.push({laneKey:e.laneKey,label:e.label,row:v,x:N,y:f.y,width:t.width,height:t.height,section:{x:N-A,y:f.y-j,width:t.width+A*2,height:t.height+j+A}})}if(d.size===0)return{positions:d,bands:a,bbox:{minX:0,minY:0,maxX:0,maxY:0}};let l=1/0,p=1/0,k=-1/0,g=-1/0;for(const e of o.nodes){const t=d.get(e.key);t&&(l=Math.min(l,t.x),p=Math.min(p,t.y),k=Math.max(k,t.x+e.box.width),g=Math.max(g,t.y+e.box.height))}for(const e of a)l=Math.min(l,e.section.x),p=Math.min(p,e.section.y),k=Math.max(k,e.section.x+e.section.width),g=Math.max(g,e.section.y+e.section.height);return{positions:d,bands:a,bbox:{minX:l,minY:p,maxX:k,maxY:g}}}function P(o,h){return o+(h-o)/2}function Z(o,h){const d=new Map(o.nodes.map(r=>[r.key,r])),c=[];for(const r of o.edges){const x=h.get(r.from),s=d.get(r.from),n=h.get(r.to),a=d.get(r.to);if(!x||!s||!n||!a)continue;const i={x:x.x+s.box.width,y:x.y+s.anchorY},l={x:n.x,y:n.y+a.anchorY},k=n.y>x.y+s.box.height&&l.x>i.x+$;c.push({id:r.id,label:r.label,variant:r.kind==="tail"?"dashed":"solid",from:i,to:l,...k?{waypoints:[{x:i.x+$/2,y:P(x.y+s.box.height,n.y)},{x:l.x-$/2,y:P(x.y+s.box.height,n.y)}]}:{}})}return c}export{D as FORK_CARD_BAND,$ as NODE_GAP_X,X as NODE_GAP_Y,j as SECTION_LIFT,A as SECTION_PAD,S as TARGET_ASPECT,W as cardBoxOf,z as forkCardKey,U as graphOf,P as gutterBetween,C as labelGap,V as layoutGraph,Z as projectEdges,F as tailKeyOf};
|
package/dist/flow-layout.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { Flow, FlowStep, Viewport } from "./manifest.types";
|
|
2
|
-
import { type RowExtent } from "./screen-frame";
|
|
3
2
|
export interface StepNode {
|
|
4
3
|
key: string;
|
|
5
4
|
flat: number;
|
|
@@ -24,12 +23,8 @@ export declare function layOut(flow: Flow, viewportOf: (id: string) => Viewport)
|
|
|
24
23
|
lanes: Lane[];
|
|
25
24
|
nodes: StepNode[];
|
|
26
25
|
};
|
|
27
|
-
export declare function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
width: number;
|
|
33
|
-
} | null;
|
|
34
|
-
}
|
|
35
|
-
export declare function rowExtents(lanes: readonly Lane[], tails?: RowTails): RowExtent[];
|
|
26
|
+
export declare function locateNode(lanes: readonly Lane[], key: string): {
|
|
27
|
+
lane: Lane;
|
|
28
|
+
position: number;
|
|
29
|
+
isLast: boolean;
|
|
30
|
+
} | null;
|
package/dist/flow-layout.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function f(e){return e===null?"trunk":`branch:${e}`}function d(e,l){const o=[],t=(u,n,s)=>u.map((c,a)=>({key:`${s}:${a}:${c.route}`,flat:o.length+a,number:n+a,step:c,viewport:l(c.viewport)})),r=t(e.steps,1,f(null));o.push(...r);const i=[{kind:"trunk",nodes:r}];for(const[u,n]of e.branches.entries()){const s=t(n.steps,n.from+2,f(n.id));o.push(...s),i.push({kind:"branch",id:n.id,index:u,label:n.label,from:n.from,fork:r[n.from]?.flat??null,nodes:s})}return{lanes:i,nodes:o}}function p(e,l){for(const o of e){const t=o.nodes.findIndex(r=>r.key===l);if(t!==-1)return{lane:o,position:t,isLast:t===o.nodes.length-1}}return null}export{f as laneKey,d as layOut,p as locateNode};
|
package/dist/frame.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare function RouteFrame({ src, title, width, height, theme, frameRef,
|
|
|
21
21
|
onLoad?: (frame: HTMLIFrameElement) => void;
|
|
22
22
|
}): import("react").JSX.Element;
|
|
23
23
|
export interface MirrorPick {
|
|
24
|
+
wants: "append" | "fork";
|
|
24
25
|
text: string;
|
|
25
26
|
href: string | null;
|
|
26
27
|
rect: {
|
|
@@ -30,15 +31,19 @@ export interface MirrorPick {
|
|
|
30
31
|
height: number;
|
|
31
32
|
};
|
|
32
33
|
}
|
|
33
|
-
export declare
|
|
34
|
-
export declare function MirrorFrame({ srcdoc, title, width, height, theme, connect, }: {
|
|
34
|
+
export declare function MirrorFrame({ srcdoc, title, width, height, theme, connect, onMenu, }: {
|
|
35
35
|
srcdoc: string;
|
|
36
36
|
title: string;
|
|
37
37
|
width: number;
|
|
38
38
|
height: number;
|
|
39
39
|
theme: "light" | "dark";
|
|
40
40
|
connect?: {
|
|
41
|
+
busy?: boolean;
|
|
41
42
|
onPick: (pick: MirrorPick | null) => void;
|
|
42
43
|
onHover: (pick: MirrorPick | null) => void;
|
|
43
44
|
};
|
|
45
|
+
onMenu?: (at: {
|
|
46
|
+
x: number;
|
|
47
|
+
y: number;
|
|
48
|
+
}) => void;
|
|
44
49
|
}): import("react").JSX.Element;
|
package/dist/frame.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as
|
|
1
|
+
"use client";import{jsx as m,jsxs as _}from"react/jsx-runtime";import{useContext as W,useEffect as y,useRef as C}from"react";import{CanvasGesturesContext as X,EDGE_VAR as g}from"./canvas-gestures";import{cx as z}from"./cx";import{ctaOf as Y,CTA_MAX_DEPTH as B,semanticCtaOf as G}from"./cta";import{isElement as H,readProp as $}from"./dom-realm";import{PickBox as K}from"./pick-box";import{mapPoint as F,mapRect as U}from"./pick-highlight";function nt({label:e,meta:o,children:c,className:i}){return _("div",{className:z("overflow-hidden border-zinc-300 bg-white dark:border-zinc-700 dark:bg-zinc-900",i),style:{borderWidth:`var(${g}, 1px)`,borderStyle:"solid",borderRadius:`min(calc(var(${g}, 1px) * 8), 6%)`,boxShadow:`0 calc(var(${g}, 1px) * 1) calc(var(${g}, 1px) * 2) rgb(0 0 0 / 0.05)`},children:[(e||o)&&_("div",{className:"flex items-center justify-between gap-3 border-b border-zinc-200 bg-zinc-50 px-3 py-1.5 text-[11px] font-medium tracking-wide text-zinc-600 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300",children:[m("span",{className:"truncate",children:e}),o&&m("span",{className:"truncate font-mono text-[10px] text-zinc-500 dark:text-zinc-400",children:o})]}),c]})}function ot({theme:e,children:o,padded:c=!0,editing:i}){return m("div",{className:z(e==="dark"&&"dark"),children:m(K,{enabled:i,className:z("bg-background text-foreground",c&&"p-6"),children:o})})}function ct({src:e,title:o,width:c,height:i,theme:u,frameRef:b,onLoad:x}){const p=C(null);return y(()=>{if(u===void 0)return;const n=p.current;if(!n)return;const f=()=>{try{n.contentDocument?.documentElement.classList.toggle("dark",u==="dark")}catch{}};f(),n.addEventListener("load",f);const h=[300,1200,2500].map(a=>setTimeout(f,a));return()=>{n.removeEventListener("load",f);for(const a of h)clearTimeout(a)}},[u,e]),m("iframe",{ref:n=>{p.current=n,b?.(n)},src:e,title:o,width:c,height:i,onLoad:n=>x?.(n.currentTarget),className:"border-0 bg-white",style:{width:c,height:i}})}function I(e){return(e.tagName==="INPUT"?String($(e,"value")??""):"")||e.getAttribute("aria-label")||e.getAttribute("title")||e.textContent||""}function M(e,o,c){const i=c?e.ownerDocument.defaultView:null,u=c?e.getBoundingClientRect():null;return{tag:e.tagName.toUpperCase(),role:e.getAttribute("role")?.toLowerCase()??null,type:e.getAttribute("type")?.toLowerCase()??null,href:e.hasAttribute("href"),onclick:e.hasAttribute("onclick"),disabled:e.hasAttribute("disabled")||e.getAttribute("aria-disabled")==="true",cursor:i?i.getComputedStyle(e).cursor:"auto",name:c?I(e):"",share:u&&o>0?u.width*u.height/o:1}}function A(e){const o=C(e);return y(()=>{o.current=e},[e]),o}function at({srcdoc:e,title:o,width:c,height:i,theme:u,connect:b,onMenu:x}){const p=C(null),n=A(b),f=A(x),h=A(W(X));return y(()=>{const a=p.current;if(!a)return;const D=a.clientWidth*a.clientHeight,v=t=>{if(!H(t))return null;const r=[];for(let l=t;l&&r.length<B;l=l.parentElement)r.push(l);const s=r.map(l=>M(l,D,!1)),d=G(s);if(d)return r[s.indexOf(d)]??null;const j=r.map(l=>M(l,D,!0)),T=Y(j);return T?r[j.indexOf(T)]??null:null},k=(t,r="append")=>{const s=t.closest("a[href]"),d=I(t).trim().replace(/\s+/g," ").slice(0,80);return{wants:r,text:d,href:s?String($(s,"href")):null,rect:U(t,a)}},N=t=>{const r=t.target;H(r)&&r.closest("a[href]")&&t.preventDefault();const s=v(r);n.current?.onPick(s?k(s):null)};let E=null,L=null;const O=t=>{if(!n.current||t.target===L)return;L=t.target;const r=v(t.target);r!==E&&(E=r,n.current.onHover(r?k(r):null))},R=()=>{E=null,L=null,n.current?.onHover(null)},P=t=>{const r=n.current&&!n.current.busy?n.current:null,s=r?v(t.target):null;if(s&&r){t.preventDefault(),r.onPick(k(s,"fork"));return}const d=f.current;d&&(t.preventDefault(),d(F(t.clientX,t.clientY,a)))},S=t=>{if(!t.ctrlKey&&!t.metaKey)return;t.preventDefault();const r=F(t.clientX,t.clientY,a);h.current?.zoomAtClient(r.x,r.y,t.deltaY,t.deltaMode)},w=()=>{try{const t=a.contentDocument;if(!t)return;t.documentElement.classList.toggle("dark",u==="dark"),t.addEventListener("click",N,!0),t.addEventListener("mouseover",O,!0),t.addEventListener("mouseleave",R),t.addEventListener("wheel",S,{passive:!1}),t.addEventListener("contextmenu",P,!0)}catch(t){console.warn("[workbench] no se pudo armar el espejo",t)}};return w(),a.addEventListener("load",w),()=>{a.removeEventListener("load",w);try{const t=a.contentDocument;t?.removeEventListener("click",N,!0),t?.removeEventListener("mouseover",O,!0),t?.removeEventListener("mouseleave",R),t?.removeEventListener("wheel",S),t?.removeEventListener("contextmenu",P,!0)}catch{}}},[u,e,c,i]),m("iframe",{ref:p,srcDoc:e,title:o,sandbox:"allow-same-origin","data-workbench-mirror":"",tabIndex:-1,width:c,height:i,className:"border-0 bg-white",style:{width:c,height:i}})}export{nt as FrameShell,ot as InlineSurface,at as MirrorFrame,ct as RouteFrame};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface JourneyAct {
|
|
2
|
+
kind: "append" | "fork";
|
|
3
|
+
verb: string;
|
|
4
|
+
button: string;
|
|
5
|
+
menu: string;
|
|
6
|
+
chip: string | null;
|
|
7
|
+
hint: string;
|
|
8
|
+
title: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const APPEND: JourneyAct;
|
|
11
|
+
export declare const FORK: JourneyAct;
|
|
12
|
+
export declare const ONLY_FROM_TRUNK = "Una rama sale siempre de un paso del tronco.";
|
|
13
|
+
export declare const ONLY_AT_THE_END = "S\u00F3lo se a\u00F1ade al final de una fila; desde el medio, se ramifica.";
|
|
14
|
+
export declare const AT_CAP = "El recorrido ya tiene todas las pantallas que caben.";
|
|
15
|
+
export declare const HAS_BRANCHES = "Este paso tiene ramas; qu\u00EDtalas primero.";
|
|
16
|
+
export declare const EN_CURSO = "Hay una escritura en curso.";
|
|
17
|
+
export declare const CLICK = "clic";
|
|
18
|
+
export declare const RIGHT_CLICK = "clic derecho";
|
|
19
|
+
export declare const NOWHERE = "aqu\u00ED no se puede a\u00F1adir";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a={kind:"append",verb:"seguir",button:"Seguir aqu\xED",menu:"Seguir aqu\xED",chip:null,hint:"la nueva pantalla sigue a \xE9sta",title:"A\xF1ade una pantalla despu\xE9s de \xE9sta."},e={kind:"fork",verb:"ramificar",button:"Nueva rama",menu:"Nueva rama desde aqu\xED",chip:"+ rama",hint:"la nueva pantalla empieza una rama desde \xE9sta",title:"Empieza una rama desde este paso."},t="Una rama sale siempre de un paso del tronco.",n="S\xF3lo se a\xF1ade al final de una fila; desde el medio, se ramifica.",r="El recorrido ya tiene todas las pantallas que caben.",s="Este paso tiene ramas; qu\xEDtalas primero.",o="Hay una escritura en curso.",i="clic",u="clic derecho",p="aqu\xED no se puede a\xF1adir";export{a as APPEND,r as AT_CAP,i as CLICK,o as EN_CURSO,e as FORK,s as HAS_BRANCHES,p as NOWHERE,n as ONLY_AT_THE_END,t as ONLY_FROM_TRUNK,u as RIGHT_CLICK};
|
package/dist/manifest.types.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type ViewerConfig = {
|
|
|
13
13
|
[key in string]: string;
|
|
14
14
|
};
|
|
15
15
|
viewports: Array<Viewport>;
|
|
16
|
+
maxFlowSteps: number;
|
|
16
17
|
};
|
|
17
18
|
export type TokenKind = "color" | "value";
|
|
18
19
|
export type Token = {
|
|
@@ -74,6 +75,7 @@ export type FlowStep = {
|
|
|
74
75
|
via: string | null;
|
|
75
76
|
viewport: string;
|
|
76
77
|
note: string | null;
|
|
78
|
+
notice: string | null;
|
|
77
79
|
spec: string | null;
|
|
78
80
|
exists: boolean;
|
|
79
81
|
};
|
|
@@ -174,4 +176,4 @@ export type OverlayManifest = {
|
|
|
174
176
|
routes: Array<OverlayRoute>;
|
|
175
177
|
surfaces: Array<OverlaySurface>;
|
|
176
178
|
};
|
|
177
|
-
export declare const MANIFEST_VERSION =
|
|
179
|
+
export declare const MANIFEST_VERSION = 3;
|
package/dist/manifest.types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const o=
|
|
1
|
+
const o=3;export{o as MANIFEST_VERSION};
|
package/dist/mirror.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
import { type Sketch } from "./screen-sketch";
|
|
1
2
|
export declare const QUIET_MS = 500;
|
|
2
3
|
export declare const CAPTURE_MAX_MS = 8000;
|
|
3
4
|
export declare const CAPTURE_TOTAL_MS = 30000;
|
|
4
5
|
export declare const MIRROR_MAX_BYTES = 2000000;
|
|
5
6
|
export declare function serializeMirror(doc: Document, route: string, origin: string): string;
|
|
6
7
|
export declare function whenQuiet(doc: Document, quietMs?: number, maxMs?: number): Promise<"quiet" | "restless">;
|
|
7
|
-
export
|
|
8
|
+
export interface Capture {
|
|
9
|
+
srcdoc: string;
|
|
10
|
+
sketch: Sketch | null;
|
|
11
|
+
}
|
|
12
|
+
export declare function captureMirror(frame: HTMLIFrameElement, route: string): Promise<Capture | null>;
|
package/dist/mirror.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";const
|
|
1
|
+
"use client";import{readSketch as a}from"./screen-sketch";const h=500,d=8e3,w=3e4,m=2e6;function f(t,c,s){const r=t.documentElement;if(!r)throw new Error("documento sin ra\xEDz");const e=r.cloneNode(!0);for(const i of Array.from(e.querySelectorAll("script, noscript, base")))i.remove();for(const i of[e,...Array.from(e.querySelectorAll("*"))])for(const l of Array.from(i.attributes))l.name.toLowerCase().startsWith("on")&&i.removeAttribute(l.name);const o=t.createElement("base");o.setAttribute("href",new URL(c,s).href);const n=e.querySelector("head");return n?n.insertBefore(o,n.firstChild):e.insertBefore(o,e.firstChild),`${t.doctype?`<!DOCTYPE ${t.doctype.name}>`:"<!DOCTYPE html>"}${e.outerHTML}`}function p(t,c=h,s=d){return new Promise(r=>{let e,o;const n=new MutationObserver(()=>{clearTimeout(e),e=setTimeout(()=>u("quiet"),c)}),u=l=>{n.disconnect(),clearTimeout(e),clearTimeout(o),r(l)},i=t.documentElement;if(!i){r("restless");return}n.observe(i,{childList:!0,subtree:!0,attributes:!0,characterData:!0}),e=setTimeout(()=>u("quiet"),c),o=setTimeout(()=>u("restless"),s)})}async function y(t,c){try{const s=t.contentDocument;if(!s||await p(s)==="restless")return null;const r=t.contentDocument;if(!r)return null;const e=t.contentWindow?.location.pathname??c,o=f(r,e,window.location.origin);if(o.length>m)return null;let n=null;try{n=a(r,{width:t.contentWindow?.innerWidth??t.clientWidth,height:t.contentWindow?.innerHeight??t.clientHeight})}catch{}return{srcdoc:o,sketch:n}}catch{return null}}export{d as CAPTURE_MAX_MS,w as CAPTURE_TOTAL_MS,m as MIRROR_MAX_BYTES,h as QUIET_MS,y as captureMirror,f as serializeMirror,p as whenQuiet};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type NodeActionId = "demote" | "activate" | "refresh" | "append" | "fork" | "remove";
|
|
2
|
+
export interface NodeAction {
|
|
3
|
+
id: NodeActionId;
|
|
4
|
+
label: string;
|
|
5
|
+
chip: string | null;
|
|
6
|
+
title: string;
|
|
7
|
+
blocked: string | null;
|
|
8
|
+
edits: boolean;
|
|
9
|
+
}
|
|
10
|
+
export type NodeFrame = "live" | "mirrored" | "restless" | "unbuilt" | "loading";
|
|
11
|
+
export interface NodeSituation {
|
|
12
|
+
frame: NodeFrame;
|
|
13
|
+
lane: "trunk" | "branch";
|
|
14
|
+
isLast: boolean;
|
|
15
|
+
isForkPoint: boolean;
|
|
16
|
+
removable: boolean;
|
|
17
|
+
canEdit: boolean;
|
|
18
|
+
atCap: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface NodeMenuContents {
|
|
21
|
+
actions: NodeAction[];
|
|
22
|
+
note: string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare function nodeActions(at: NodeSituation): NodeMenuContents;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{journeyMoves as n}from"./flow-edit";import{APPEND as s,FORK as p,HAS_BRANCHES as o}from"./journey-acts";function d(e){const l=[];if(e.frame==="live"?l.push({id:"demote",label:"Volver a espejo",chip:"Volver a espejo",title:"Congela la p\xE1gina en un espejo est\xE1tico y libera el documento vivo.",blocked:null,edits:!1}):e.frame==="mirrored"?l.push({id:"activate",label:"Activar la p\xE1gina",chip:"Activar",title:"Monta la p\xE1gina real para interactuar con ella.",blocked:null,edits:!1},{id:"refresh",label:"Actualizar el espejo",chip:"Actualizar",title:"Vuelve a cargar la ruta y captura un espejo nuevo.",blocked:null,edits:!1}):e.frame==="restless"&&l.push({id:"refresh",label:"Reintentar el espejo",chip:"Reintentar espejo",title:"La p\xE1gina no termin\xF3 de asentarse; vuelve a intentarlo.",blocked:null,edits:!1}),!e.canEdit)return{actions:l,note:null};const i=n(e,e.atCap),t=(a,r)=>l.push({id:r,label:a.menu,chip:a.chip,title:a.title,blocked:null,edits:!0});return i.append&&t(s,"append"),i.fork&&t(p,"fork"),e.isLast&&e.removable&&l.push({id:"remove",label:"Quitar la pantalla",chip:"quitar",title:e.isForkPoint?o:"Quita esta pantalla del recorrido.",blocked:e.isForkPoint?o:null,edits:!0}),{actions:l,note:i.why}}export{d as nodeActions};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NodeAction } from "./node-actions";
|
|
2
|
+
export declare function NodeMenu({ title, at, actions, note, busy, onRun, onClose, }: {
|
|
3
|
+
title: string;
|
|
4
|
+
at: {
|
|
5
|
+
top: number;
|
|
6
|
+
left: number;
|
|
7
|
+
};
|
|
8
|
+
actions: readonly NodeAction[];
|
|
9
|
+
note: string | null;
|
|
10
|
+
busy: boolean;
|
|
11
|
+
onRun: (action: NodeAction) => void;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as i,jsxs as m}from"react/jsx-runtime";import{useEffect as w,useRef as N}from"react";import{cx as E}from"./cx";import{useFloating as A}from"./floating";import{EN_CURSO as b}from"./journey-acts";function I({title:d,at:a,actions:c,note:f,busy:h,onRun:k,onClose:u}){const p=N(null),l=(e,r)=>{const t=Array.from(o.current?.querySelectorAll('[role="menuitem"]')??[]);if(t.length===0)return;e.preventDefault();const n=t.findIndex(z=>z===document.activeElement),y=r==="first"?0:r==="last"?t.length-1:(n+r+t.length)%t.length;t[y]?.focus()},g={top:a.top,left:a.left,width:0,height:0},{ref:o,placed:s,style:x}=A(g,u);return w(()=>{s&&(p.current??o.current)?.focus()},[s]),m("div",{ref:o,role:"menu",tabIndex:-1,"aria-label":`Acciones de ${d}`,onKeyDown:e=>{e.key==="Escape"?u():e.key==="ArrowDown"?l(e,1):e.key==="ArrowUp"?l(e,-1):e.key==="Home"?l(e,"first"):e.key==="End"&&l(e,"last")},className:"fixed z-[2147483050] w-52 rounded-md border border-zinc-200 bg-white p-1 shadow-lg dark:border-zinc-700 dark:bg-zinc-900",style:s?x:{...x,visibility:"hidden"},children:[i("p",{role:"presentation",className:"truncate px-1.5 pt-1 pb-1.5 text-[11px] font-medium text-zinc-500 dark:text-zinc-400",children:d}),c.length===0?i("p",{role:"presentation",className:"px-1.5 pb-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:"Nada que hacer aqu\xED todav\xEDa."}):c.map((e,r)=>{const t=e.edits&&h,n=e.blocked!==null||t;return m("button",{ref:r===0?p:void 0,type:"button",role:"menuitem","aria-disabled":n||void 0,"aria-label":t?`${e.label} \u2014 ${b}`:void 0,title:t?b:e.title,onClick:()=>{n||k(e)},className:E("block w-full rounded px-1.5 py-1 text-left text-[12px] text-zinc-700 dark:text-zinc-200",n?"opacity-40":"hover:bg-zinc-100 dark:hover:bg-zinc-800"),children:[e.label,e.blocked&&i("span",{className:"mt-0.5 block text-[10px] leading-snug text-amber-700 dark:text-amber-300",children:e.blocked})]},e.id)}),f&&i("p",{role:"presentation",className:"px-1.5 pt-1 pb-0.5 text-[10px] leading-snug text-zinc-500 dark:text-zinc-400",children:f})]})}export{I as NodeMenu};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type CanvasDetail } from "../canvas-gestures";
|
|
2
|
+
import { type CanvasViewport } from "../flow-canvas";
|
|
3
|
+
import { laneRefOf, type ComposerTarget } from "../flow-edit";
|
|
4
|
+
import { type Frames } from "../flow-frames";
|
|
5
|
+
import { type Lane, type StepNode } from "../flow-layout";
|
|
6
|
+
import { type MirrorPick } from "../frame";
|
|
7
|
+
import type { Flow, Viewport } from "../manifest.types";
|
|
8
|
+
import { type CardDraft, type CardStatus } from "./flow-cards";
|
|
9
|
+
export interface BoardComposer {
|
|
10
|
+
card: {
|
|
11
|
+
at: ComposerTarget;
|
|
12
|
+
draft: CardDraft;
|
|
13
|
+
status: CardStatus;
|
|
14
|
+
} | null;
|
|
15
|
+
cardViewport: Viewport | null;
|
|
16
|
+
routesList: string;
|
|
17
|
+
busy: boolean;
|
|
18
|
+
onDraft: (changes: Partial<CardDraft>) => void;
|
|
19
|
+
onSubmit: () => void;
|
|
20
|
+
onCancelCard: () => void;
|
|
21
|
+
onGhost: (lane: ReturnType<typeof laneRefOf>, after: number, flat: number, anchorRoute: string) => void;
|
|
22
|
+
onFork: (position: number, flat: number, anchorRoute: string) => void;
|
|
23
|
+
onRemove: (lane: ReturnType<typeof laneRefOf>) => void;
|
|
24
|
+
connect: boolean;
|
|
25
|
+
onPick: (node: StepNode, pick: MirrorPick | null) => void;
|
|
26
|
+
onHoverPick: (node: StepNode, pick: MirrorPick | null) => void;
|
|
27
|
+
}
|
|
28
|
+
export declare function CanvasBoard({ flow, lanes, themes, nonce, editing, frames, active, fitKey, shape, onCapture, onActivate, onRefresh, onDemote, registerLiveFrame, composer, onViewportChange, onDetailChange, }: {
|
|
29
|
+
flow: Flow;
|
|
30
|
+
lanes: Lane[];
|
|
31
|
+
themes: Array<"light" | "dark">;
|
|
32
|
+
nonce: number;
|
|
33
|
+
editing: boolean;
|
|
34
|
+
frames: Frames;
|
|
35
|
+
active: {
|
|
36
|
+
key: string;
|
|
37
|
+
slot: number;
|
|
38
|
+
} | null;
|
|
39
|
+
fitKey: string;
|
|
40
|
+
shape: string;
|
|
41
|
+
onCapture: (key: string, ticket: number, frame: HTMLIFrameElement) => void;
|
|
42
|
+
onActivate: (key: string, slot: number) => void;
|
|
43
|
+
onRefresh: (key: string) => void;
|
|
44
|
+
onDemote: () => void;
|
|
45
|
+
registerLiveFrame: (key: string, slot: number, frame: HTMLIFrameElement | null) => void;
|
|
46
|
+
composer: BoardComposer | null;
|
|
47
|
+
onViewportChange: (viewport: CanvasViewport) => void;
|
|
48
|
+
onDetailChange: (detail: CanvasDetail) => void;
|
|
49
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{Fragment as S,jsx as a,jsxs as h}from"react/jsx-runtime";import{useCallback as Pe,useContext as oe,useEffect as X,useMemo as F,useRef as se,useState as ce}from"react";import{CanvasDetailContext as de}from"../canvas-gestures";import{useManifest as Te}from"../config-context";import{CanvasChrome as j,FlowCanvas as _e}from"../flow-canvas";import{cx as P}from"../cx";import{atCap as Be,forkResolvesTo as Ye,isForkPoint as Ge,laneRefOf as V,refKey as ue}from"../flow-edit";import{frameOf as T}from"../flow-frames";import{cardBoxOf as me,forkCardKey as he,graphOf as He,layoutGraph as Xe,projectEdges as Ve,tailKeyOf as fe}from"../flow-graph";import{laneKey as xe,locateNode as qe}from"../flow-layout";import{MirrorFrame as Ie,RouteFrame as Ue}from"../frame";import{frameUrl as Je}from"../manifest";import{ScreenFrame as pe}from"../screen-frame";import{NodeMenu as Qe}from"../node-menu";import{AT_CAP as We,EN_CURSO as Ze}from"../journey-acts";import{nodeActions as be}from"../node-actions";import{CHIP as ke}from"../screen-toolbar";import{GhostChip as et,NewScreenCard as tt,SketchCard as nt,SpecCard as rt,Stage as at,TailPlaceholder as it}from"./flow-cards";function q({children:f,always:k=!1}){return oe(de)==="near"||k?a(S,{children:f}):null}function lt({far:f,near:k}){return a(S,{children:oe(de)==="far"?f:k})}function ot(f){return new Date(f).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}const st=200,I=[{box:"border-zinc-950/10 bg-zinc-950/[0.03] dark:border-white/10 dark:bg-white/[0.03]",text:"text-zinc-500 dark:text-zinc-400"},{box:"border-sky-500/25 bg-sky-500/[0.04]",text:"text-sky-700 dark:text-sky-300"},{box:"border-violet-500/25 bg-violet-500/[0.08] dark:bg-violet-500/[0.08]",text:"text-violet-700 dark:text-violet-300"},{box:"border-emerald-500/25 bg-emerald-500/[0.04] dark:bg-emerald-500/[0.08]",text:"text-emerald-700 dark:text-emerald-300"}];function Et({flow:f,lanes:k,themes:_,nonce:U,editing:ge,frames:M,active:x,fitKey:ye,shape:O,onCapture:we,onActivate:ve,onRefresh:Ce,onDemote:Ne,registerLiveFrame:ze,composer:l,onViewportChange:$e,onDetailChange:Ee}){const{config:J}=Te(),B=Be(f,J.maxFlowSteps),c=l?.card??null,g=l?.cardViewport??null,[y,K]=ce(null),Se=(e,r)=>{const i=T(M,e);return x!==null&&x.key===e.key&&x.slot===r?"live":i.kind==="mirrored"?"mirrored":i.kind==="restless"?"restless":i.kind==="unbuilt"?"unbuilt":"loading"},Q=(e,r,i,t)=>({frame:Se(r,t),lane:e.kind,isLast:i===e.nodes.length-1,isForkPoint:e.kind==="trunk"&&Ge(f,i),removable:e.kind==="branch"||e.nodes.length>1,canEdit:l!==null,atCap:B}),w=y&&qe(k,y.key),z=w?.lane.nodes[w.position]??null,Y=y&&w&&z?be(Q(w.lane,z,w.position,y.slot)):null;X(()=>{K(e=>e===null?e:null)},[O,U]);const W=(e,r,i)=>{l?.onPick(e,null),K({at:i,key:e.key,slot:r})},Z=(e,r)=>{const{lane:i,node:t,position:o,slot:n}=r;if(e.blocked===null&&!(e.edits&&l&&(l.busy||l.card!==null)))switch(e.id){case"demote":Ne();break;case"activate":ve(t.key,n);break;case"refresh":Ce(t.key);break;case"append":l?.onGhost(V(i),o,t.flat,t.step.route);break;case"fork":l?.onFork(o,t.flat,t.step.route);break;case"remove":l?.onRemove(V(i));break}},ee=l!==null,A=F(()=>{if(!ee)return;const e=new Map;return c&&!c.at.fork&&g&&e.set(ue(c.at.lane),me(g)),{tail:e,forkCard:c?.at.fork&&g?{fork:c.at.flat,...me(g),label:c.draft.branchLabel.trim()||"nueva rama"}:null}},[ee,k,c,g]),{graph:R,positions:$,bands:D,bbox:v}=F(()=>{const e=He(k,A);return{graph:e,...Xe(e)}},[k,A]),je=F(()=>{const e=new Map;let r=0;for(const i of D)i.laneKey!==xe(null)&&(e.set(i.laneKey,1+r%(I.length-1)),r+=1);return e},[D]),Oe=F(()=>Ve(R,$),[R,$]),[Ke,te]=ce(null),ne=se(0),L=Pe(e=>{ne.current+=1,te({key:`${e}:0`,tick:ne.current})},[]),G=c?c.at.fork?he(c.at.flat):fe(ue(c.at.lane)):null;X(()=>{G?L(G):te(null)},[G,L]);const re=se(null);X(()=>{const e=re.current;if(re.current=O,e===null||e===O)return;const r=new Set(e.split("|")),i=new Set(O.split("|")),t=[...i].filter(n=>n&&!r.has(n)),o=[...r].filter(n=>n&&!i.has(n));t.length===1&&o.length===0&&t[0]&&L(t[0])},[O,L]);const Ae=(e,r,i)=>{const{step:t,viewport:o}=e,n=T(M,e),m=x!==null&&x.key===e.key&&x.slot===r,d=n.kind==="capturing"?n.ticket:null,p=d!==null&&r===0,b=m||n.kind==="restless"&&r===0||p;return h(S,{children:[t.via&&h("span",{className:"sr-only",children:["V\xEDa \xAB",t.via,"\xBB"]}),a(pe,{viewport:o,scale:1,bare:!0,editing:ge&&n.kind!=="unbuilt",children:b?a(Ue,{src:t.route,title:`${f.title} \xB7 ${t.label}`,width:o.width,height:o.height,theme:i,frameRef:s=>ze(e.key,r,s),onLoad:p&&d!==null?s=>we(e.key,d,s):void 0},U):a(lt,{far:a(nt,{viewport:o,theme:i,sketch:n.kind==="mirrored"?n.sketch:null,tone:n.kind==="unbuilt"?"unbuilt":"mirror"}),near:n.kind==="unbuilt"?a(rt,{step:t,viewport:o,theme:i}):n.kind==="mirrored"?a(Ie,{srcdoc:n.srcdoc,title:`${f.title} \xB7 ${t.label} (espejo)`,width:o.width,height:o.height,theme:i,connect:l?.connect?{busy:l.busy||l.card!==null,onPick:s=>l.onPick(e,s),onHover:s=>l.onHoverPick(e,s)}:void 0,onMenu:s=>W(e,r,s)}):a(at,{viewport:o,theme:i,className:"flex items-center justify-center",children:a("p",{className:"max-w-lg px-10 text-center text-2xl leading-relaxed text-zinc-400 dark:text-zinc-500",children:n.kind==="capturing"?"Cargando la ruta real\u2026":n.kind==="restless"?"Sin espejo \u2014 el paso sigue en vivo en el tablero claro.":"En cola \u2014 cada paso se carga una sola vez y queda como espejo."})})})})]})},Fe=(e,r,i,t,o)=>{const{step:n,viewport:m}=r,d=T(M,r),p=x!==null&&x.key===r.key&&x.slot===t,b=d.kind==="capturing"&&t===0,s=be(Q(e,r,i,t)).actions.filter(u=>u.chip!==null);return h(S,{children:[a(j,{anchor:"bottom left",clamp:!0,style:{bottom:"100%"},children:h("p",{title:`${r.number}. ${n.label} \xB7 ${n.route}`,className:P("flex items-baseline gap-1.5 overflow-hidden whitespace-nowrap pb-1 text-[11px] font-medium",d.kind==="unbuilt"?"text-amber-700 dark:text-amber-300":p?"text-emerald-600 dark:text-emerald-400":"text-zinc-600 dark:text-zinc-300"),children:[h("span",{className:"shrink-0 truncate",children:[r.number,". ",n.label]}),a(q,{children:h("span",{className:"min-w-0 truncate font-mono text-[10px] text-zinc-500 dark:text-zinc-400",children:[n.route," \xB7 ",m.label," ",m.width,"\xD7",m.height]})})]})}),a(q,{always:p||d.kind==="restless"||b,children:h(j,{clamp:!0,style:{top:"100%"},children:[(p||d.kind==="mirrored"||d.kind==="restless"||d.kind==="unbuilt"||b)&&h("div",{className:"mt-2 flex flex-wrap items-center gap-2",children:[d.kind==="unbuilt"?a("span",{className:"text-[11px] text-amber-700 dark:text-amber-300",title:"La ruta no existe todav\xEDa; el cuadro muestra la especificaci\xF3n. Se vuelve un espejo real en el siguiente `workbench scan` despu\xE9s de que la p\xE1gina aterrice.",children:"Sin p\xE1gina todav\xEDa"}):p?a("span",{className:"text-[11px] font-medium text-emerald-600 dark:text-emerald-400",children:"En vivo"}):d.kind==="mirrored"?h("span",{className:"text-[11px] text-zinc-500 dark:text-zinc-400",title:"Espejo est\xE1tico de la p\xE1gina real \u2014 sin peticiones al servidor.",children:["Espejo \xB7 ",ot(d.capturedAt)]}):d.kind==="restless"?a("span",{className:"text-[11px] text-amber-700 dark:text-amber-300",title:"La p\xE1gina no termin\xF3 de asentarse, as\xED que el cuadro sigue en vivo.",children:"En vivo \u2014 sin espejo"}):a("span",{className:"text-[11px] text-zinc-500 dark:text-zinc-400",children:"Cargando\u2026"}),s.map(u=>{const N=u.edits&&l!==null&&(l.busy||l.card!==null),E=u.blocked??(N?Ze:null);return E!==null?a("button",{type:"button",className:P(ke,"opacity-40"),"aria-disabled":"true","aria-label":`${u.chip} \u2014 ${E}`,onClick:()=>{},title:E,children:u.chip},u.id):a("button",{type:"button",className:ke,onClick:()=>Z(u,{lane:e,node:r,position:i,slot:t}),title:u.title,children:u.chip},u.id)})]}),h("div",{className:"mt-2 flex items-start justify-between gap-2",children:[h("div",{className:"flex min-w-0 flex-col gap-1",children:[n.note&&a("p",{className:"text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:n.note}),n.notice&&a("p",{className:"text-[11px] leading-snug text-amber-700 dark:text-amber-300",children:n.notice})]}),a("span",{className:"ml-auto flex shrink-0 items-center gap-2",children:d.kind!=="unbuilt"?h(S,{children:[h("a",{href:n.route,target:"_blank",rel:"noreferrer",className:"font-mono text-[11px] text-zinc-500 underline underline-offset-2 hover:text-zinc-900 dark:hover:text-zinc-100",children:[n.route," \u2197"]}),a("a",{href:Je(J,{route:n.route,viewport:m.id,theme:o}),target:"_blank",rel:"noreferrer",className:"text-[11px] text-zinc-500 underline underline-offset-2 hover:text-zinc-900 dark:hover:text-zinc-100",children:"aislado \u2197"})]}):a("span",{className:"font-mono text-[11px] text-zinc-400 dark:text-zinc-500",children:n.route})})]})]})})]})},ae=(e,r,i)=>!c||!g||!l?null:a(pe,{viewport:g,scale:1,bare:!0,children:e===0?a(tt,{viewport:g,theme:r,fork:i,routesList:l.routesList,draft:c.draft,status:c.status,onDraft:l.onDraft,onSubmit:l.onSubmit,onCancel:l.onCancelCard}):a(it,{viewport:g,theme:r,writing:c.status==="writing"})}),H=F(()=>new Map(R.nodes.map(e=>[e.key,e])),[R]),Me=_.length>1,ie=v.maxY-Math.min(v.minY,0)+st,C=[],le=[];for(const[e,r]of _.entries()){const i=e*ie;Me&&C.push({key:`theme:${e}`,x:v.minX,y:v.minY+i,width:400,height:1,behind:!0,element:null,chrome:a(j,{anchor:"bottom left",style:{bottom:"100%"},children:a("p",{className:"text-[11px] font-medium uppercase tracking-wider text-zinc-500 dark:text-zinc-400",children:r==="light"?"Claro":"Oscuro"})})});for(const t of D){const o=I[je.get(t.laneKey)??0]??I[0];C.push({key:`section:${t.laneKey}:${e}`,x:t.section.x,y:t.section.y+i,width:t.section.width,height:t.section.height,behind:!0,element:a("div",{className:P("h-full w-full rounded-2xl border",o.box)}),chrome:a(j,{anchor:"bottom left",clamp:!0,style:{bottom:"100%",left:4},children:a("p",{title:t.label??"Tronco",className:P("truncate whitespace-nowrap pb-1 text-[11px] font-medium",o.text),children:t.label??"Tronco"})})})}for(const t of k){const o=xe(t.kind==="trunk"?null:t.id);for(const[b,s]of t.nodes.entries()){const u=$.get(s.key),N=H.get(s.key);if(!u||!N)continue;const E=T(M,s);C.push({key:`${s.key}:${e}`,x:u.x,y:u.y+i,width:N.box.width,height:N.box.height,alwaysRender:E.kind==="capturing"||E.kind==="restless"||x?.key===s.key&&x.slot===e,element:Ae(s,e,r),onContextMenu:Le=>W(s,e,Le),chromeLabel:`${s.number}. ${s.step.label} \xB7 ${s.step.route}`,chrome:Fe(t,s,b,e,r)})}const n=A?.tail.get(o),m=t.nodes[t.nodes.length-1],d=fe(o),p=n?$.get(d):void 0;if(n&&p&&l)C.push({key:`${d}:${e}`,x:p.x,y:p.y+i,width:n.width,height:n.height,alwaysRender:!0,element:ae(e,r,!1)});else if(l){const b=D.find(N=>N.laneKey===o),s=m?$.get(m.key):b&&{x:b.x,y:b.y},u=m?H.get(m.key):void 0;s&&C.push({key:`add:${o}:${e}`,x:s.x+(u?.box.width??0),y:s.y+i,width:1,height:u?.box.height??1,element:null,chrome:a(q,{children:a(j,{anchor:"bottom left",style:{top:"50%",left:12},children:a(et,{label:"+ A\xF1adir pantalla",disabled:l.busy||l.card!==null||B,reason:B?We:void 0,onOpen:()=>l.onGhost(V(t),t.nodes.length-1,m?.flat??0,m?.step.route??"")})})})})}}if(A?.forkCard&&c){const t=he(A.forkCard.fork),o=$.get(t),n=H.get(t);if(o&&n){const m=Ye(f,f.steps[c.at.after]?.route??"")!==c.at.after;C.push({key:`${t}:${e}`,x:o.x,y:o.y+i,width:n.box.width,height:n.box.height,alwaysRender:!0,element:ae(e,r,!0),chrome:m?a(j,{anchor:"bottom left",style:{bottom:"100%"},children:a("p",{className:"max-w-[26rem] text-[11px] leading-snug text-amber-700 dark:text-amber-300",children:"El tronco repite esta ruta; el motor dibuja la rama desde su primera aparici\xF3n."})}):void 0})}}for(const t of Oe)le.push({...t,id:`${t.id}:${e}`,from:{x:t.from.x,y:t.from.y+i},to:{x:t.to.x,y:t.to.y+i},...t.waypoints?{waypoints:t.waypoints.map(o=>({x:o.x,y:o.y+i}))}:{}})}const Re={minX:v.minX,minY:v.minY,maxX:v.maxX,maxY:v.maxY+(_.length-1)*ie},De=a(_e,{nodes:C,edges:le,bbox:Re,fitKey:ye,reveal:Ke,onViewportChange:e=>{K(r=>r===null?r:null),$e(e)},onDetailChange:Ee,label:`Lienzo del recorrido ${f.title}`,className:"bg-zinc-50 [--wb-canvas-bg:#fafafa] dark:bg-zinc-950 dark:[--wb-canvas-bg:#09090b]"});return h(S,{children:[De,y&&w&&z&&Y&&a(Qe,{title:`${z.number}. ${z.step.label}`,at:{top:y.at.y,left:y.at.x},actions:Y.actions,note:Y.note,busy:l!==null&&(l.busy||l.card!==null),onRun:e=>{K(null),Z(e,{lane:w.lane,node:z,position:w.position,slot:y.slot})},onClose:()=>K(null)})]})}export{Et as CanvasBoard};
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
import type { FlowStep, Viewport } from "../manifest.types";
|
|
3
|
+
import type { Sketch } from "../screen-sketch";
|
|
3
4
|
export declare function Stage({ viewport, theme, className, children, }: {
|
|
4
5
|
viewport: Viewport;
|
|
5
6
|
theme: "light" | "dark";
|
|
6
7
|
className?: string;
|
|
7
8
|
children: ReactNode;
|
|
8
9
|
}): import("react").JSX.Element;
|
|
10
|
+
export declare function SketchCard({ sketch, viewport, theme, tone, }: {
|
|
11
|
+
sketch: Sketch | null;
|
|
12
|
+
viewport: Viewport;
|
|
13
|
+
theme: "light" | "dark";
|
|
14
|
+
tone: "mirror" | "unbuilt";
|
|
15
|
+
}): import("react").JSX.Element;
|
|
9
16
|
export declare function SpecCard({ step, viewport, theme, }: {
|
|
10
17
|
step: FlowStep;
|
|
11
18
|
viewport: Viewport;
|
|
@@ -39,12 +46,9 @@ export declare function TailPlaceholder({ viewport, theme, writing, }: {
|
|
|
39
46
|
theme: "light" | "dark";
|
|
40
47
|
writing: boolean;
|
|
41
48
|
}): import("react").JSX.Element;
|
|
42
|
-
export declare
|
|
43
|
-
export declare function GhostCard({ width, height, scale, label, disabled, onOpen, }: {
|
|
44
|
-
width: number;
|
|
45
|
-
height: number;
|
|
46
|
-
scale: number;
|
|
49
|
+
export declare function GhostChip({ label, disabled, reason, onOpen, }: {
|
|
47
50
|
label: string;
|
|
48
51
|
disabled: boolean;
|
|
52
|
+
reason?: string;
|
|
49
53
|
onOpen: () => void;
|
|
50
54
|
}): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as n}from"react/jsx-runtime";import"react";import{cx as b}from"../cx";
|
|
1
|
+
"use client";import{jsx as e,jsxs as n}from"react/jsx-runtime";import"react";import{cx as b}from"../cx";function x({viewport:a,theme:l,className:t,children:d}){return e("div",{className:b(l==="dark"&&"dark","bg-zinc-50 dark:bg-zinc-900",t),style:{width:a.width,height:a.height},children:d})}function w({sketch:a,viewport:l,theme:t,tone:d}){return n(x,{viewport:l,theme:t,className:"relative overflow-hidden bg-white dark:bg-zinc-900",children:[d==="unbuilt"&&e("div",{className:"absolute inset-x-0 top-0 bg-amber-400",style:{height:l.height*.012}}),a&&a.boxes.length>0?e("svg",{"aria-hidden":!0,viewBox:"0 0 100 100",preserveAspectRatio:"none",className:"h-full w-full",style:{padding:l.width*.04},children:a.boxes.map((r,c)=>e("rect",{x:r.x*100,y:r.y*100,width:r.w*100,height:r.h*100,rx:.6,className:r.kind==="text"?"fill-zinc-400/70 dark:fill-zinc-500/70":r.kind==="media"?"fill-zinc-300 dark:fill-zinc-700":"fill-zinc-200/60 dark:fill-zinc-800/60"},c))}):e("div",{className:"flex h-full w-full items-center justify-center",children:e("p",{className:"text-zinc-300 dark:text-zinc-600",style:{fontSize:l.width*.06},children:d==="unbuilt"?"Por construir":"Sin vista previa"})})]})}function y({step:a,viewport:l,theme:t}){return n(x,{viewport:l,theme:t,className:"flex flex-col gap-6 p-12 text-zinc-700 dark:text-zinc-200",children:[n("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-2",children:[e("span",{className:"rounded-md border border-dashed border-amber-500 px-3 py-1 text-xl font-medium uppercase tracking-wider text-amber-700 dark:text-amber-300",children:"Por construir"}),e("span",{className:"break-all font-mono text-2xl text-zinc-500 dark:text-zinc-400",children:a.route})]}),e("h3",{className:"text-5xl font-semibold leading-tight text-zinc-900 dark:text-zinc-100",children:a.label}),e("p",{className:"max-w-3xl text-3xl leading-relaxed",children:a.spec??"Sin especificaci\xF3n todav\xEDa. A\xF1\xE1dela con la tarjeta al crear la pantalla, o con `spec` en workbench.config.json, para que este cuadro diga qu\xE9 debe hacer la pantalla."})]})}const m="mt-2 w-full rounded-md border border-zinc-300 bg-white px-4 py-3 text-3xl text-zinc-900 disabled:opacity-60 dark:border-zinc-600 dark:bg-zinc-800 dark:text-zinc-100",p="text-2xl font-medium uppercase tracking-wider text-zinc-400";function v({viewport:a,theme:l,fork:t,routesList:d,draft:r,status:c,onDraft:o,onSubmit:g,onCancel:u}){const h=typeof c=="object"&&"written"in c,s=c==="writing"||h,k=r.route.trim().length>0&&(!t||r.branchLabel.trim().length>0);return n(x,{viewport:a,theme:l,className:"flex flex-col gap-6 overflow-hidden p-12 text-zinc-700 dark:text-zinc-200",children:[n("div",{className:"flex flex-wrap items-center gap-x-4 gap-y-2",children:[e("span",{className:"rounded-md border border-dashed border-sky-500 px-3 py-1 text-xl font-medium uppercase tracking-wider text-sky-700 dark:text-sky-300",children:t?"Nueva rama":"Nueva pantalla"}),r.via.trim()&&n("span",{className:"text-2xl text-zinc-500 dark:text-zinc-400",children:["v\xEDa \xAB",r.via.trim(),"\xBB"]})]}),n("form",{className:"flex min-h-0 flex-1 flex-col gap-5",onSubmit:i=>{i.preventDefault(),g()},onKeyDown:i=>{i.key==="Escape"&&(i.stopPropagation(),u())},children:[t&&n("label",{className:"block",children:[e("span",{className:p,children:"Nombre de la rama"}),e("input",{value:r.branchLabel,onChange:i=>o({branchLabel:i.target.value}),placeholder:"Tier 2",disabled:s,className:m})]}),n("label",{className:"block",children:[e("span",{className:p,children:"Ruta"}),e("input",{value:r.route,onChange:i=>o({route:i.target.value}),list:d,placeholder:"/pantalla",autoFocus:!0,disabled:s,className:b(m,"font-mono")})]}),n("label",{className:"block",children:[e("span",{className:p,children:"Nombre"}),e("input",{value:r.label,onChange:i=>o({label:i.target.value}),placeholder:"Qu\xE9 pantalla es",disabled:s,className:m})]}),n("label",{className:"block",children:[e("span",{className:p,children:"Qu\xE9 debe hacer"}),e("textarea",{value:r.spec,onChange:i=>o({spec:i.target.value}),rows:4,placeholder:"Se muestra en el tablero mientras la p\xE1gina no exista.",disabled:s,className:b(m,"resize-none")})]}),typeof c=="object"&&"error"in c&&e("p",{role:"alert",className:"text-2xl leading-snug text-red-700 dark:text-red-300",children:c.error}),h&&e("p",{role:"status",className:"text-2xl text-emerald-700 dark:text-emerald-300",children:c.written??"Escrito \xB7 esperando el mapa\u2026"}),n("div",{className:"mt-auto flex items-center gap-4",children:[e("button",{type:"submit",disabled:s||!k,className:"rounded-md bg-zinc-900 px-6 py-3 text-2xl font-medium text-white disabled:opacity-40 dark:bg-zinc-100 dark:text-zinc-900",children:c==="writing"?"Escribiendo\u2026":"A\xF1adir"}),e("button",{type:"button",onClick:u,disabled:c==="writing",className:"rounded-md border border-zinc-300 px-6 py-3 text-2xl text-zinc-700 disabled:opacity-40 dark:border-zinc-600 dark:text-zinc-200",children:h?"Cerrar":"Cancelar"})]})]})]})}function C({viewport:a,theme:l,writing:t}){return e(x,{viewport:a,theme:l,className:"flex items-center justify-center",children:e("p",{className:"max-w-lg px-10 text-center text-2xl leading-relaxed text-zinc-400 dark:text-zinc-500",children:t?"Escribiendo\u2026":"Pantalla nueva \u2014 se edita en el tablero claro."})})}function j({label:a,disabled:l,reason:t,onOpen:d}){return e("button",{type:"button",onClick:d,disabled:l,title:t,"aria-label":t?`${a} \u2014 ${t}`:void 0,className:"whitespace-nowrap rounded-md border border-dashed border-zinc-400 bg-white/80 px-2 py-1 text-[11px] font-medium text-zinc-500 backdrop-blur hover:border-sky-400 hover:text-sky-600 disabled:opacity-40 dark:border-zinc-600 dark:bg-zinc-900/80 dark:text-zinc-400 dark:hover:border-sky-500 dark:hover:text-sky-400",children:a})}export{j as GhostChip,v as NewScreenCard,w as SketchCard,y as SpecCard,x as Stage,C as TailPlaceholder};
|
package/dist/panels/flows.d.ts
CHANGED
|
@@ -2,9 +2,8 @@ import type { ComponentIndex } from "../hover-inspect";
|
|
|
2
2
|
import type { Flow } from "../manifest.types";
|
|
3
3
|
import { type PickTarget } from "../pick-highlight";
|
|
4
4
|
import type { Pin, SaveFlowInput, SaveFlowResult } from "../pin";
|
|
5
|
-
import { type Zoom } from "../screen-frame";
|
|
6
5
|
import { type ThemeMode } from "../screen-toolbar";
|
|
7
|
-
export declare function FlowsPanel({ flow, componentIndex, editing, onPin, onHover, onToggleEdit, pinCount, onOpenRequest, showChrome, initialViewport,
|
|
6
|
+
export declare function FlowsPanel({ flow, componentIndex, editing, onPin, onHover, onToggleEdit, pinCount, onOpenRequest, showChrome, initialViewport, initialTheme, routes, onSaveFlow, }: {
|
|
8
7
|
flow: Flow;
|
|
9
8
|
componentIndex: ComponentIndex;
|
|
10
9
|
editing: boolean;
|
|
@@ -15,7 +14,6 @@ export declare function FlowsPanel({ flow, componentIndex, editing, onPin, onHov
|
|
|
15
14
|
onOpenRequest: () => void;
|
|
16
15
|
showChrome: boolean;
|
|
17
16
|
initialViewport?: string;
|
|
18
|
-
initialZoom?: Zoom;
|
|
19
17
|
initialTheme?: ThemeMode;
|
|
20
18
|
routes?: string[];
|
|
21
19
|
onSaveFlow?: (input: SaveFlowInput) => Promise<SaveFlowResult>;
|
package/dist/panels/flows.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as r,jsxs as o,Fragment as Z}from"react/jsx-runtime";import{Fragment as Ye,useCallback as M,useEffect as O,useMemo as me,useRef as fe,useState as C}from"react";import{useManifest as $e}from"../config-context";import{ConnectPopover as Je}from"../connect-popover";import{useInspect as Xe}from"../edit-mode";import{appendStep as et,bodyOf as he,forkFrom as tt,forkResolvesTo as nt,hasStepAt as rt,isForkPoint as at,removeLast as it,routeOfHref as je,stepInputOf as st}from"../flow-edit";import{frameOf as ke,NO_FRAMES as Fe,nextCapture as Re,prune as ot,withFrame as Q}from"../flow-frames";import{forkPrefix as Te,laneKey as xe,layOut as lt,rowExtents as ct}from"../flow-layout";import{MirrorFrame as dt,RouteFrame as ut}from"../frame";import{frameUrl as pt,viewportById as ae}from"../manifest";import{CAPTURE_TOTAL_MS as mt,captureMirror as ft,serializeMirror as ht}from"../mirror";import{Outline as kt}from"../pick-highlight";import{framedWidth as Le,rowWidth as Ae,ScreenFrame as be,STEP_GUTTER as Se,useScale as xt}from"../screen-frame";import{AUTO as ge,CHIP as L,ScreenToolbar as bt}from"../screen-toolbar";import{GHOST_WIDTH as ve,GhostCard as gt,NewScreenCard as De,SpecCard as vt,Stage as wt,TailPlaceholder as Ve}from"./flow-cards";function yt(i){return new Date(i).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})}const Be={route:"",label:"",spec:"",via:"",branchLabel:""};function Oe(i){return i.kind==="trunk"?{kind:"trunk"}:{kind:"branch",index:i.index,id:i.id}}function we(i){return xe(i.kind==="trunk"?null:i.id)}function Nt({flow:i,lanes:I,scale:m,theme:g,nonce:ie,editing:Y,slot:E,frames:se,active:G,onCapture:oe,onActivate:le,onRefresh:J,onDemote:ce,registerLiveFrame:A,composer:n}){const{config:P}=$e();return o("div",{className:"space-y-6",children:[I.map(s=>{const X=s.kind==="branch"&&s.fork!==null?Ae(Te(I,s.fork),m)+Se:0,$=Oe(s),q=n?.card&&!n.card.at.fork&&we(n.card.at.lane)===we($)?n.card:null,S=s.nodes[s.nodes.length-1];return o("div",{className:"w-max",style:X>0?{marginLeft:X}:void 0,children:[s.kind==="branch"&&o("p",{className:"mb-2 whitespace-nowrap text-[11px] font-medium text-zinc-500 dark:text-zinc-400",children:[r("span",{"aria-hidden":!0,children:"\u21B3 "}),s.label," \xB7 desde el paso ",s.from+1,s.nodes[0]?.step.via&&` \xB7 v\xEDa \xAB${s.nodes[0].step.via}\xBB`,s.fork===null&&" \u2014 ese paso no est\xE1 en el tronco"]}),o("ol",{className:"flex items-start gap-4","aria-label":s.kind==="trunk"?"Tronco":`${s.label}, desde el paso ${s.from+1}`,children:[s.nodes.map((f,v)=>{const{step:h,viewport:k}=f,c=ke(se,f),R=G!==null&&G.key===f.key&&G.slot===E,y=c.kind==="capturing"?c.ticket:null,d=y!==null&&E===0,D=R||c.kind==="restless"&&E===0||d,j=v===s.nodes.length-1,x=s.nodes[v+1]?.step.via;return o(Ye,{children:[o("li",{className:"shrink-0",children:[r(be,{viewport:k,scale:m,label:`${f.number}. ${h.label}`,editing:Y&&c.kind!=="unbuilt",children:c.kind==="unbuilt"?r(vt,{step:h,viewport:k,theme:g}):D?r(ut,{src:h.route,title:`${i.title} \xB7 ${h.label}`,width:k.width,height:k.height,theme:g,frameRef:b=>A(f.key,E,b),onLoad:d&&y!==null?b=>oe(f.key,y,b):void 0},ie):c.kind==="mirrored"?r(dt,{srcdoc:c.srcdoc,title:`${i.title} \xB7 ${h.label} (espejo)`,width:k.width,height:k.height,theme:g,connect:n?.connect&&!Y?{onPick:b=>n.onPick(f,b),onHover:b=>n.onHoverPick(f,b)}:void 0}):r(wt,{viewport:k,theme:g,className:"flex items-center justify-center",children:r("p",{className:"max-w-lg px-10 text-center text-2xl leading-relaxed text-zinc-400 dark:text-zinc-500",children:c.kind==="capturing"?"Cargando la ruta real\u2026":c.kind==="restless"?"Sin espejo \u2014 el paso sigue en vivo en el tablero claro.":"En cola \u2014 cada paso se carga una sola vez y queda como espejo."})})}),(R||c.kind==="mirrored"||c.kind==="restless"||c.kind==="unbuilt"||d)&&o("div",{className:"mt-2 flex flex-wrap items-center gap-2",style:{width:Le(k.width,m)},children:[c.kind==="unbuilt"?r("span",{className:"text-[11px] text-amber-700 dark:text-amber-300",title:"La ruta no existe todav\xEDa; el cuadro muestra la especificaci\xF3n. Se vuelve un espejo real en el siguiente `workbench scan` despu\xE9s de que la p\xE1gina aterrice.",children:"Sin p\xE1gina todav\xEDa"}):R?o(Z,{children:[r("span",{className:"text-[11px] font-medium text-emerald-600 dark:text-emerald-400",children:"En vivo"}),r("button",{type:"button",className:L,onClick:ce,children:"Volver a espejo"})]}):c.kind==="mirrored"?o(Z,{children:[o("span",{className:"text-[11px] text-zinc-500 dark:text-zinc-400",title:"Espejo est\xE1tico de la p\xE1gina real \u2014 sin peticiones al servidor.",children:["Espejo \xB7 ",yt(c.capturedAt)]}),r("button",{type:"button",className:L,onClick:()=>le(f.key,E),title:"Monta la p\xE1gina real para interactuar con ella.",children:"Activar"}),r("button",{type:"button",className:L,onClick:()=>J(f.key),title:"Vuelve a cargar la ruta y captura un espejo nuevo.",children:"Actualizar"})]}):c.kind==="restless"?o(Z,{children:[r("span",{className:"text-[11px] text-amber-700 dark:text-amber-300",title:"La p\xE1gina no termin\xF3 de asentarse, as\xED que el cuadro sigue en vivo.",children:"En vivo \u2014 sin espejo"}),r("button",{type:"button",className:L,onClick:()=>J(f.key),children:"Reintentar espejo"})]}):r("span",{className:"text-[11px] text-zinc-500 dark:text-zinc-400",children:"Cargando\u2026"}),n&&s.kind==="trunk"&&r("button",{type:"button",className:L,disabled:n.busy||n.card!==null,onClick:()=>n.onFork(v,f.flat,f.step.route),title:"Empieza una rama desde este paso.",children:"+ rama"}),n&&j&&(s.kind==="branch"||s.nodes.length>1)&&(s.kind==="trunk"&&at(i,v)?r("button",{type:"button",className:`${L} opacity-40`,"aria-disabled":"true",onClick:()=>{},title:"Este paso tiene ramas; qu\xEDtalas primero.",children:"quitar"}):r("button",{type:"button",className:L,disabled:n.busy||n.card!==null,onClick:()=>n.onRemove($),title:"Quita esta pantalla del recorrido.",children:"quitar"}))]}),o("div",{className:"mt-2 flex items-start justify-between gap-2",style:{width:Le(k.width,m)},children:[h.note&&r("p",{className:"text-[11px] leading-snug text-zinc-500 dark:text-zinc-400",children:h.note}),r("span",{className:"ml-auto flex shrink-0 items-center gap-2",children:c.kind!=="unbuilt"?o(Z,{children:[o("a",{href:h.route,target:"_blank",rel:"noreferrer",className:"font-mono text-[11px] text-zinc-500 underline underline-offset-2 hover:text-zinc-900 dark:hover:text-zinc-100",children:[h.route," \u2197"]}),r("a",{href:pt(P,{route:h.route,viewport:k.id,theme:g}),target:"_blank",rel:"noreferrer",className:"text-[11px] text-zinc-500 underline underline-offset-2 hover:text-zinc-900 dark:hover:text-zinc-100",children:"aislado \u2197"})]}):r("span",{className:"font-mono text-[11px] text-zinc-400 dark:text-zinc-500",children:h.route})})]})]}),v<s.nodes.length-1&&o("li",{"aria-hidden":x?void 0:!0,className:"relative shrink-0 self-center text-xl text-zinc-400 dark:text-zinc-600",children:[r("span",{"aria-hidden":!0,children:"\u2192"}),x&&o("span",{className:"absolute left-1/2 top-full mt-0.5 -translate-x-1/2 whitespace-nowrap text-[10px] text-zinc-500 dark:text-zinc-400",children:["\xAB",x,"\xBB"]})]})]},`${v}:${h.route}`)}),n&&o(Z,{children:[r("li",{"aria-hidden":!0,className:"shrink-0 self-center text-xl text-zinc-300 dark:text-zinc-700",children:"\u2192"}),r("li",{className:"shrink-0",children:q&&n.cardViewport?r(be,{viewport:n.cardViewport,scale:m,label:"Nueva pantalla",children:E===0?r(De,{viewport:n.cardViewport,theme:g,fork:!1,routesList:n.routesList,draft:q.draft,status:q.status,onDraft:n.onDraft,onSubmit:n.onSubmit,onCancel:n.onCancelCard}):r(Ve,{viewport:n.cardViewport,theme:g,writing:q.status==="writing"})}):r(gt,{width:ve,height:S?.viewport.height??844,scale:m,label:"+ A\xF1adir pantalla",disabled:n.busy||n.card!==null,onOpen:()=>n.onGhost($,s.nodes.length-1,S?.flat??0,S?.step.route??"")})})]})]})]},s.kind==="trunk"?"trunk":`branch:${s.id}`)}),n?.card?.at.fork&&n.cardViewport&&o("div",{className:"w-max",style:{marginLeft:Ae(Te(I,n.card.at.flat),m)+Se},children:[o("p",{className:"mb-2 whitespace-nowrap text-[11px] font-medium text-sky-600 dark:text-sky-400",children:[r("span",{"aria-hidden":!0,children:"\u21B3 "}),n.card.draft.branchLabel.trim()||"nueva rama"," \xB7 desde el paso"," ",n.card.at.after+1,nt(i,i.steps[n.card.at.after]?.route??"")!==n.card.at.after&&" \u2014 el tronco repite esta ruta; el motor dibuja la rama desde su primera aparici\xF3n"]}),r("ol",{className:"flex items-start gap-4","aria-label":"Nueva rama",children:r("li",{className:"shrink-0",children:r(be,{viewport:n.cardViewport,scale:m,label:"Nueva pantalla",children:E===0?r(De,{viewport:n.cardViewport,theme:g,fork:!0,routesList:n.routesList,draft:n.card.draft,status:n.card.status,onDraft:n.onDraft,onSubmit:n.onSubmit,onCancel:n.onCancelCard}):r(Ve,{viewport:n.cardViewport,theme:g,writing:n.card.status==="writing"})})})})]})]})}const Pe="workbench-flow-routes";function Pt({flow:i,componentIndex:I,editing:m,onPin:g,onHover:ie,onToggleEdit:Y,pinCount:E,onOpenRequest:se,showChrome:G,initialViewport:oe,initialZoom:le,initialTheme:J,routes:ce,onSaveFlow:A}){const{config:n,stats:P}=$e(),[s,X]=C(0),[$,q]=C(oe??ge),[S,f]=C(le??"fit"),[v,h]=C(J??"light"),k=fe(null),c=$===ge?null:ae(n,$),R=ae(n,void 0).id,{lanes:y,nodes:d}=me(()=>lt(i,e=>c??ae(n,e)),[i,c,n]),[D,j]=C(Fe),[x,b]=C(null),de=fe(new Map);O(()=>{j(Fe),b(null)},[s]);const ye=d.map(e=>e.key).join("|");O(()=>{j(e=>ot(e,d)),b(e=>e&&d.some(t=>t.key===e.key)?e:null)},[ye]);const Ne=fe(1);O(()=>{const e=Re(D,d);e!==null&&j(t=>{if(Re(t,d)!==e)return t;const a=Ne.current;return Ne.current+=1,Q(t,e,{kind:"capturing",ticket:a})})},[D,d]);let ee=null,te=null;for(const e of d){const t=ke(D,e);if(t.kind==="capturing"){ee=e.key,te=t.ticket;break}}O(()=>{if(ee===null||te===null)return;const e=ee,t=te,a=setTimeout(()=>{j(l=>{const u=l.get(e);return u?.kind==="capturing"&&u.ticket===t?Q(l,e,{kind:"restless"}):l})},mt);return()=>clearTimeout(a)},[ee,te]);const qe=M((e,t,a)=>{const l=d.find(u=>u.key===e)?.step.route;l&&ft(a,l).then(u=>{j(F=>{const p=F.get(e);return p?.kind!=="capturing"||p.ticket!==t?F:Q(F,e,u?{kind:"mirrored",srcdoc:u,capturedAt:Date.now()}:{kind:"restless"})})}).catch(()=>{})},[d]),ue=M(()=>{if(!x)return;const e=de.current.get(`${x.slot}:${x.key}`),t=d.find(a=>a.key===x.key)?.step.route;if(e&&t)try{const a=e.contentDocument;if(a){const l=e.contentWindow?.location.pathname??t,u=ht(a,l,window.location.origin),F=Date.now(),p=x.key;j(B=>Q(B,p,{kind:"mirrored",srcdoc:u,capturedAt:F}))}}catch{}b(null)},[x,d]),He=M((e,t)=>{ue(),b({key:e,slot:t})},[ue]),_e=M(e=>{j(t=>Q(t,e,{kind:"queued"}))},[]),Me=M((e,t,a)=>{const l=`${t}:${e}`;a?de.current.set(l,a):de.current.delete(l)},[]),U=!!A,[w,N]=C(null),[ne,H]=C(null),[V,T]=C(null),[K,re]=C(null),pe=w!==null&&typeof w.status=="object"&&"written"in w.status&&w.status.written===null&&rt(i,w.at,w.draft.route.trim());O(()=>{pe&&N(null)},[pe]);const z=w&&!pe?w:null,W=z?c??d.find(e=>e.flat===z.at.flat)?.viewport??ae(n,void 0):null,Ie=z?.status==="writing"||ne!==null&&ne.error===null;O(()=>{m&&(T(null),re(null))},[m]),O(()=>{T(null),re(null),H(e=>e&&e.error===null?null:e)},[ye,s]);const ze=e=>{for(const t of y){const a=t.nodes.findIndex(l=>l.key===e);if(a!==-1)return{ref:Oe(t),kind:t.kind,position:a,isLast:a===t.nodes.length-1}}return null},Ge=async()=>{if(!w||!A||w.status==="writing")return;const{at:e,draft:t}=w,a=e.lane.kind==="trunk"?i.steps[e.after]:i.branches[e.lane.index]?.id===e.lane.id?i.branches[e.lane.index]?.steps[e.after]:void 0;if(!a||a.route!==e.anchorRoute){N(p=>p&&{...p,status:{error:"El recorrido cambi\xF3 debajo de la tarjeta; ci\xE9rrala y vuelve a a\xF1adir la pantalla."}});return}const l=a.viewport,u=st(t,l===R?"":l),F=e.fork?tt(he(i,R),a.route,t.branchLabel.trim(),u):et(he(i,R),e.lane,u);N(p=>p&&{...p,status:"writing"});try{const p=await A({...F,id:i.id});N(B=>B&&{...B,status:p.ok?{written:p.note??null}:{error:p.error??"Fall\xF3 la escritura."}})}catch(p){N(B=>B&&{...B,status:{error:p instanceof Error?p.message:"Fall\xF3 la escritura."}})}},Ue=U?{card:z,cardViewport:W,routesList:Pe,busy:Ie,onDraft:e=>N(t=>t&&{...t,status:"editing",draft:{...t.draft,...e}}),onSubmit:()=>{Ge()},onCancelCard:()=>N(null),onGhost:(e,t,a,l)=>{T(null),N({at:{lane:e,after:t,anchorRoute:l,fork:!1,flat:a},draft:Be,status:"editing"})},onFork:(e,t,a)=>{T(null),N({at:{lane:{kind:"trunk"},after:e,anchorRoute:a,fork:!0,flat:t},draft:Be,status:"editing"})},onRemove:e=>{if(!A)return;const t=e.kind==="trunk"?e:(()=>{const a=i.branches.findIndex(l=>l.id===e.id);return a===-1?null:{kind:"branch",index:a,id:e.id}})();if(!t){H({error:"El recorrido cambi\xF3; esa fila ya no existe."});return}H({error:null}),A({...it(he(i,R),t),id:i.id}).then(a=>{a.ok?a.note&&H({error:a.note}):H({error:a.error??"Fall\xF3 la escritura."})}).catch(a=>H({error:a instanceof Error?a.message:"Fall\xF3 la escritura."}))},connect:!m,onPick:(e,t)=>{if(!t){T(null);return}const a=ze(e.key);if(!a)return;const l=je(t.href,window.location.origin),u=a.isLast?"append":a.kind==="trunk"?"fork":"blocked";T({node:e,pick:t,route:l,mode:u}),re(null)},onHoverPick:(e,t)=>{re(t?{pick:t,route:je(t.href,window.location.origin)}:null)}}:null,Ke=()=>{if(!V)return;const{node:e,pick:t,route:a,mode:l}=V,u=ze(e.key);if(T(null),!u||l==="blocked")return;const F={route:a??"",label:t.text,spec:"",via:t.text,branchLabel:t.text},p=l==="fork"?{lane:{kind:"trunk"},after:u.position,anchorRoute:e.step.route,fork:!0,flat:e.flat}:{lane:u.ref,after:u.position,anchorRoute:e.step.route,fork:!1,flat:e.flat};N({at:p,draft:F,status:"editing"})},Ce=me(()=>{if(!U)return;const e=new Map;for(const t of y){const a=t.kind==="trunk"?xe(null):xe(t.id),l=z&&!z.at.fork&&we(z.at.lane)===a?W?.width??ve:ve;e.set(a,l)}return{tail:e,forkRow:z?.at.fork&&W?{fork:z.at.flat,width:W.width}:null}},[U,y,z,W]),We=me(()=>ct(y,Ce),[y,Ce]),{ref:Ze,scale:Qe}=xt(S,We),Ee=v==="split"?["light","dark"]:[v];Xe(m,M(()=>k.current,[]),I,{onPin:g,onHover:ie},`${i.id}:${s}:${$}:${S}:${v}:${d.map(e=>ke(D,e).kind.charAt(0)).join("")}:${x?`${x.slot}.${x.key}`:"-"}`);const _=d.filter(e=>!e.step.exists).length;return o("div",{className:"flex h-full flex-col",children:[G&&r(bt,{viewport:$,onViewport:q,zoom:S,onZoom:f,theme:v,onTheme:h,actions:r("button",{type:"button",onClick:()=>X(e=>e+1),className:L,children:"Recargar cuadros"}),hint:$===ge?"Rutas reales, cargadas de una en una y congeladas como espejos; activa un paso para interactuar. Las pantallas con sesi\xF3n requieren haber iniciado sesi\xF3n aqu\xED.":`Rutas reales, los ${d.length-_} pasos a ${c?.width}px, congeladas como espejos${_>0?`; ${_} por construir`:""}.`,editing:m,onToggleEdit:Y,pinCount:E,onOpenRequest:se,isolatedBase:{tab:"flujos",flow:i.id}}),o("div",{ref:Ze,className:"min-h-0 flex-1 overflow-auto bg-zinc-50 p-6 dark:bg-zinc-950",children:[o("header",{className:"mb-5 max-w-2xl",children:[o("div",{className:"flex items-center gap-2",children:[r("h2",{className:"text-lg font-semibold text-zinc-900 dark:text-zinc-100",children:i.title}),i.origin==="spider"&&r("span",{className:"rounded bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",title:"Descubierto siguiendo los enlaces reales de la app. A\xF1\xE1dele una pantalla y queda declarado en workbench.config.json con este mismo id.",children:"descubierto"}),i.branches.length>0&&o("span",{className:"rounded bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",children:[i.branches.length," ",i.branches.length===1?"rama":"ramas"]})]}),i.description&&r("p",{className:"mt-1 text-sm text-zinc-600 dark:text-zinc-400",children:i.description}),U&&r("p",{className:"mt-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:"Haz clic en un bot\xF3n o enlace de un espejo para a\xF1adir la pantalla a la que lleva; cada fila termina en \xAB+ A\xF1adir pantalla\xBB."}),ne?.error&&r("p",{role:"alert",className:"mt-1 text-[11px] text-red-700 dark:text-red-300",children:ne.error}),_>0&&o("p",{className:"mt-1 text-[11px] text-amber-700 dark:text-amber-300",children:[_," ",_===1?"paso por construir":"pasos por construir"," \u2014 se muestran sus especificaciones; se vuelven espejos reales cuando sus p\xE1ginas aterricen."]}),P.untraceableHrefs>0&&o("p",{className:"mt-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:[P.untraceableHrefs," enlaces con destino calculado no se pudieron rastrear, as\xED que puede faltar alg\xFAn paso."]}),P.droppedFlows>0&&o("p",{className:"mt-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:[P.droppedFlows," recorridos adicionales encontrados y no mostrados; declara los que importen en workbench.config.json."]})]}),U&&r("datalist",{id:Pe,children:(ce??[]).map(e=>r("option",{value:e},e))}),r("div",{ref:k,tabIndex:-1,children:r("div",{className:"space-y-8",children:Ee.map((e,t)=>o("div",{children:[Ee.length>1&&r("p",{className:"mb-2 text-[11px] font-medium uppercase tracking-wider text-zinc-500",children:e==="light"?"Claro":"Oscuro"}),r(Nt,{flow:i,lanes:y,scale:Qe,theme:e,nonce:s,editing:m,slot:t,frames:D,active:x,onCapture:qe,onActivate:He,onRefresh:_e,onDemote:ue,registerLiveFrame:Me,composer:Ue})]},t===0?"a":"b"))})})]}),K&&!V&&!m&&r(kt,{rect:K.pick.rect,tone:"sky",label:`\xAB${K.pick.text||"control"}\xBB${K.route?` \u2192 ${K.route}`:""}`}),V&&r(Je,{pick:V.pick,route:V.route,mode:V.mode,onConfirm:Ke,onCancel:()=>{T(null),k.current?.focus()}})]})}export{Pt as FlowsPanel};
|
|
1
|
+
"use client";import{jsx as i,jsxs as y}from"react/jsx-runtime";import{useCallback as z,useEffect as C,useMemo as He,useRef as _,useState as m}from"react";import{useManifest as Se}from"../config-context";import{ConnectPopover as Ie}from"../connect-popover";import{useInspect as qe}from"../edit-mode";import{actionFor as Me,connectGestures as Pe,appendStep as _e,atCap as Be,bodyOf as Q,connectIntent as Ve,forkFrom as We,hasStepAt as Ue,laneRefOf as Ke,removeLast as Ge,routeOfHref as Ye,stepInputOf as Je}from"../flow-edit";import{frameOf as se,NO_FRAMES as ie,nextCapture as ce,prune as Qe,withFrame as O}from"../flow-frames";import{EN_CURSO as Xe}from"../journey-acts";import{layOut as Ze,locateNode as et}from"../flow-layout";import{viewportById as B}from"../manifest";import{CAPTURE_TOTAL_MS as tt,captureMirror as nt,serializeMirror as rt}from"../mirror";import{Outline as ot}from"../pick-highlight";import{readSketch as at}from"../screen-sketch";import{AUTO as X,CHIP as st,ScreenToolbar as it}from"../screen-toolbar";import"./flow-cards";import{CanvasBoard as ct}from"./flow-canvas-board";const le={route:"",label:"",spec:"",via:"",branchLabel:""},ue="workbench-flow-routes";function Nt({flow:o,componentIndex:de,editing:b,onPin:pe,onHover:me,onToggleEdit:fe,pinCount:he,onOpenRequest:ke,showChrome:be,initialViewport:xe,initialTheme:ge,routes:ve,onSaveFlow:A}){const{config:$,stats:D}=Se(),[L,ye]=m(0),[N,ze]=m(xe??X),[H,Ce]=m(ge??"light"),[$e,Ne]=m("near"),Z=_(null),V=_(null),S=N===X?null:B($,N),I=B($,void 0).id,{lanes:ee,nodes:c}=He(()=>Ze(o,e=>S??B($,e)),[o,S,$]),[T,x]=m(ie),[u,q]=m(null),W=_(new Map);C(()=>{x(ie),q(null)},[L]);const U=c.map(e=>e.key).join("|");C(()=>{x(e=>Qe(e,c)),q(e=>e&&c.some(t=>t.key===e.key)?e:null)},[U]);const te=_(1);C(()=>{const e=ce(T,c);e!==null&&x(t=>{if(ce(t,c)!==e)return t;const n=te.current;return te.current+=1,O(t,e,{kind:"capturing",ticket:n})})},[T,c]);let M=null,P=null;for(const e of c){const t=se(T,e);if(t.kind==="capturing"){M=e.key,P=t.ticket;break}}C(()=>{if(M===null||P===null)return;const e=M,t=P,n=setTimeout(()=>{x(r=>{const s=r.get(e);return s?.kind==="capturing"&&s.ticket===t?O(r,e,{kind:"restless"}):r})},tt);return()=>clearTimeout(n)},[M,P]);const Re=z((e,t,n)=>{const r=c.find(s=>s.key===e)?.step.route;r&&nt(n,r).then(s=>{x(p=>{const a=p.get(e);return a?.kind!=="capturing"||a.ticket!==t?p:O(p,e,s?{kind:"mirrored",srcdoc:s.srcdoc,sketch:s.sketch,capturedAt:Date.now()}:{kind:"restless"})})}).catch(()=>{})},[c]),K=z(()=>{if(!u)return;const e=W.current.get(`${u.slot}:${u.key}`),t=c.find(n=>n.key===u.key)?.step.route;if(e&&t)try{const n=e.contentDocument;if(n){const r=e.contentWindow?.location.pathname??t,s=rt(n,r,window.location.origin);let p=null;try{p=at(n,{width:e.contentWindow?.innerWidth??e.clientWidth,height:e.contentWindow?.innerHeight??e.clientHeight})}catch{}const a=Date.now(),h=u.key;x(Le=>O(Le,h,{kind:"mirrored",srcdoc:s,sketch:p,capturedAt:a}))}}catch{}q(null)},[u,c]),we=z((e,t)=>{K(),q({key:e,slot:t})},[K]),je=z(e=>{x(t=>O(t,e,{kind:"queued"}))},[]),Fe=z((e,t,n)=>{const r=`${t}:${e}`;n?W.current.set(r,n):W.current.delete(r)},[]),G=!!A,[l,d]=m(null),[E,R]=m(null),[k,f]=m(null),[g,w]=m(null),Y=l!==null&&typeof l.status=="object"&&"written"in l.status&&l.status.written===null&&Ue(o,l.at,l.draft.route.trim());C(()=>{Y&&d(null)},[Y]);const j=l&&!Y?l:null,Ae=j?S??c.find(e=>e.flat===j.at.flat)?.viewport??B($,void 0):null,J=j?.status==="writing"||E!==null&&E.error===null;C(()=>{b&&(f(null),w(null))},[b]),C(()=>{f(null),w(null),R(e=>e&&e.error===null?null:e)},[U,L]);const Te=z(()=>{f(e=>e===null?e:null),w(e=>e===null?e:null),V.current?.open&&(V.current.open=!1)},[]),ne=(e,t)=>{const n=re(e.key);if(!n)return null;const r=Ye(t.href,window.location.origin);return{loc:n,route:r,intent:Ve(o,{lane:n.kind,position:n.position,isLast:n.isLast},r,Be(o,$.maxFlowSteps))}},re=e=>{const t=et(ee,e);return t&&{ref:Ke(t.lane),kind:t.lane.kind,...t}},Ee=async()=>{if(!l||!A||l.status==="writing")return;const{at:e,draft:t}=l,n=e.lane.kind==="trunk"?o.steps[e.after]:o.branches[e.lane.index]?.id===e.lane.id?o.branches[e.lane.index]?.steps[e.after]:void 0;if(!n||n.route!==e.anchorRoute){d(a=>a&&{...a,status:{error:"El recorrido cambi\xF3 debajo de la tarjeta; ci\xE9rrala y vuelve a a\xF1adir la pantalla."}});return}const r=n.viewport,s=Je(t,r===I?"":r),p=e.fork?We(Q(o,I),n.route,t.branchLabel.trim(),s):_e(Q(o,I),e.lane,s);d(a=>a&&{...a,status:"writing"});try{const a=await A({...p,id:o.id});d(h=>h&&{...h,status:a.ok?{written:a.note??null}:{error:a.error??"Fall\xF3 la escritura."}})}catch(a){d(h=>h&&{...h,status:{error:a instanceof Error?a.message:"Fall\xF3 la escritura."}})}},Oe=G?{card:j,cardViewport:Ae,routesList:ue,busy:J,onDraft:e=>d(t=>t&&{...t,status:"editing",draft:{...t.draft,...e}}),onSubmit:()=>{Ee()},onCancelCard:()=>d(null),onGhost:(e,t,n,r)=>{f(null),d({at:{lane:e,after:t,anchorRoute:r,fork:!1,flat:n},draft:le,status:"editing"})},onFork:(e,t,n)=>{f(null),d({at:{lane:{kind:"trunk"},after:e,anchorRoute:n,fork:!0,flat:t},draft:le,status:"editing"})},onRemove:e=>{if(!A)return;const t=e.kind==="trunk"?e:(()=>{const n=o.branches.findIndex(r=>r.id===e.id);return n===-1?null:{kind:"branch",index:n,id:e.id}})();if(!t){R({error:"El recorrido cambi\xF3; esa fila ya no existe."});return}R({error:null}),A({...Ge(Q(o,I),t),id:o.id}).then(n=>{n.ok?n.note&&R({error:n.note}):R({error:n.error??"Fall\xF3 la escritura."})}).catch(n=>R({error:n instanceof Error?n.message:"Fall\xF3 la escritura."}))},connect:!b,onPick:(e,t)=>{if(!t){f(null);return}if(J||j!==null)return;const n=ne(e,t);if(!n)return;const r=Me(n.intent,t.wants);if(t.wants==="fork"&&r?.kind==="fork"){w(null),oe(e,n.loc,t,n.route,r);return}f({node:e,pick:t,route:n.route,intent:n.intent}),w(null)},onHoverPick:(e,t)=>{const n=t?ne(e,t):null;w(t&&n?{pick:t,route:n.route,intent:n.intent,parked:J||j!==null}:null)}}:null,oe=(e,t,n,r,s)=>{const p={route:r??"",label:n.text,spec:"",via:n.text,branchLabel:o.branches.some(h=>h.label===n.text)?"":n.text},a=s.kind==="fork"?{lane:{kind:"trunk"},after:t.position,anchorRoute:e.step.route,fork:!0,flat:e.flat}:{lane:t.ref,after:t.position,anchorRoute:e.step.route,fork:!1,flat:e.flat};d({at:a,draft:p,status:"editing"})},De=e=>{if(!k)return;const{node:t,pick:n,route:r}=k,s=re(t.key);f(null),s&&oe(t,s,n,r,e)},ae=H==="split"?["light","dark"]:[H];qe(b,z(()=>Z.current,[]),de,{onPin:pe,onHover:me},`${o.id}:${L}:${N}:${H}:${$e}:${c.map(e=>se(T,e).kind.charAt(0)).join("")}:${u?`${u.slot}.${u.key}`:"-"}`);const F=c.filter(e=>!e.step.exists).length,v=[F>0&&`${F} ${F===1?"paso por construir":"pasos por construir"} \u2014 se muestran sus especificaciones; se vuelven espejos reales cuando sus p\xE1ginas aterricen.`,D.untraceableHrefs>0&&`${D.untraceableHrefs} enlaces con destino calculado no se pudieron rastrear, as\xED que puede faltar alg\xFAn paso.`,D.droppedFlows>0&&`${D.droppedFlows} recorridos adicionales encontrados y no mostrados; declara los que importen en workbench.config.json.`,o.steps.some(e=>e.notice)&&"Este recorrido se muestra recortado; abre un paso para ver por qu\xE9."].filter(e=>typeof e=="string");return y("div",{className:"flex h-full flex-col",children:[be&&i(it,{viewport:N,onViewport:ze,zoomDisabledReason:"El lienzo tiene su propio zoom, abajo a la izquierda.",theme:H,onTheme:Ce,actions:i("button",{type:"button",onClick:()=>ye(e=>e+1),className:st,children:"Recargar cuadros"}),hint:N===X?"Rutas reales, cargadas de una en una y congeladas como espejos; activa un paso para interactuar. Arrastra el fondo para moverte; Ctrl/\u2318 + rueda para acercar; con el lienzo enfocado, flechas para moverte, + y \u2212 para el zoom, 0 para ajustar.":`Rutas reales, los ${c.length-F} pasos a ${S?.width}px, congeladas como espejos${F>0?`; ${F} por construir`:""}. Arrastra el fondo para moverte; Ctrl/\u2318 + rueda para acercar; con el lienzo enfocado, flechas para moverte, + y \u2212 para el zoom, 0 para ajustar.`,editing:b,onToggleEdit:fe,pinCount:he,onOpenRequest:ke,isolatedBase:{tab:"flujos",flow:o.id}}),y("div",{className:"flex items-center gap-2 border-b border-zinc-200 bg-zinc-50 px-6 py-2 dark:border-zinc-800 dark:bg-zinc-950",children:[i("h2",{className:"shrink-0 text-sm font-semibold text-zinc-900 dark:text-zinc-100",children:o.title}),o.origin==="spider"&&i("span",{className:"shrink-0 rounded bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",title:"Descubierto siguiendo los enlaces reales de la app. A\xF1\xE1dele una pantalla y queda declarado en workbench.config.json con este mismo id.",children:"descubierto"}),o.branches.length>0&&y("span",{className:"shrink-0 rounded bg-zinc-100 px-1.5 py-0.5 text-[10px] font-medium text-zinc-600 dark:bg-zinc-800 dark:text-zinc-300",children:[o.branches.length," ",o.branches.length===1?"rama":"ramas"]}),y("details",{ref:V,className:"group relative min-w-0 flex-1",children:[y("summary",{className:"cursor-pointer list-none truncate text-[11px] text-zinc-500 marker:content-none [&::-webkit-details-marker]:hidden dark:text-zinc-400",children:[i("span",{className:"underline decoration-dotted underline-offset-2",children:v.length>0?v[0]:o.description??"Sobre este recorrido"}),v.length>1&&y("span",{className:"ml-1 rounded bg-amber-100 px-1 text-[10px] font-medium text-amber-800 dark:bg-amber-900/40 dark:text-amber-200",children:["+",v.length-1,i("span",{className:"sr-only",children:" avisos m\xE1s"})]})]}),y("div",{className:"absolute z-20 mt-1 max-w-2xl rounded-md border border-zinc-200 bg-white p-3 shadow-lg dark:border-zinc-700 dark:bg-zinc-900",children:[o.description&&i("p",{className:"text-sm text-zinc-600 dark:text-zinc-400",children:o.description}),v.map(e=>i("p",{className:"mt-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:e},e)),G&&i("p",{className:"mt-1 text-[11px] text-zinc-500 dark:text-zinc-400",children:"Haz clic en un bot\xF3n o enlace de un espejo para a\xF1adir la pantalla a la que lleva; cada fila termina en \xAB+ A\xF1adir pantalla\xBB."})]})]}),v.length>0&&i("ul",{className:"sr-only",children:v.map(e=>i("li",{children:e},e))}),E?.error&&i("p",{role:"alert",className:"min-w-0 flex-1 truncate text-[11px] text-red-700 dark:text-red-300",title:E.error,children:E.error}),G&&i("datalist",{id:ue,children:(ve??[]).map(e=>i("option",{value:e},e))})]}),i("div",{ref:Z,tabIndex:-1,className:"min-h-0 flex-1",children:i(ct,{flow:o,lanes:ee,themes:ae,nonce:L,editing:b,frames:T,active:u,fitKey:`${N}:${ae.length}`,shape:U,onCapture:Re,onActivate:we,onRefresh:je,onDemote:K,registerLiveFrame:Fe,composer:Oe,onViewportChange:Te,onDetailChange:Ne})}),g&&!k&&!b&&i(ot,{rect:g.pick.rect,tone:"sky",label:`\xAB${g.pick.text||"control"}\xBB${g.route?` \u2192 ${g.route}`:""}`,hint:g.parked?Xe:Pe(g.intent)}),k&&i(Ie,{pick:k.pick,route:k.route,intent:k.intent,onConfirm:De,onCancel:()=>f(e=>e===k?null:e)})]})}export{Nt as FlowsPanel};
|
package/dist/pick-highlight.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export declare function mapRect(element: Element, frame: HTMLIFrameElement | nul
|
|
|
14
14
|
width: number;
|
|
15
15
|
height: number;
|
|
16
16
|
};
|
|
17
|
+
export declare function mapPoint(x: number, y: number, frame: HTMLIFrameElement): {
|
|
18
|
+
x: number;
|
|
19
|
+
y: number;
|
|
20
|
+
};
|
|
17
21
|
export declare function describeNode(node: ResolvedNode): string;
|
|
18
22
|
declare const TONES: {
|
|
19
23
|
readonly amber: {
|
|
@@ -25,7 +29,7 @@ declare const TONES: {
|
|
|
25
29
|
readonly tag: "bg-sky-400 text-sky-950";
|
|
26
30
|
};
|
|
27
31
|
};
|
|
28
|
-
export declare function Outline({ rect, label, tone, }: {
|
|
32
|
+
export declare function Outline({ rect, label, hint, tone, }: {
|
|
29
33
|
rect: {
|
|
30
34
|
top: number;
|
|
31
35
|
left: number;
|
|
@@ -33,6 +37,7 @@ export declare function Outline({ rect, label, tone, }: {
|
|
|
33
37
|
height: number;
|
|
34
38
|
};
|
|
35
39
|
label: string;
|
|
40
|
+
hint?: string;
|
|
36
41
|
tone?: keyof typeof TONES;
|
|
37
42
|
}): import("react").JSX.Element;
|
|
38
43
|
export declare function PickHighlight({ target }: {
|
package/dist/pick-highlight.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as
|
|
1
|
+
"use client";import{jsx as r,jsxs as l}from"react/jsx-runtime";import{ZOOM_ATTR as a}from"./canvas-gestures";import{cx as c}from"./cx";import{SCALE_ATTR as p}from"./screen-frame";const d=25,u=44;function i(t,n){const e=t.closest(`[${n}]`)?.getAttribute(n),o=Number(e);return Number.isFinite(o)&&o>0?o:1}function y(t,n){const e=t.getBoundingClientRect();if(!n)return{top:e.top,left:e.left,width:e.width,height:e.height};const o=i(n,p)*i(n,a),s=n.getBoundingClientRect();return{top:s.top+e.top*o,left:s.left+e.left*o,width:e.width*o,height:e.height*o}}function N(t,n,e){const o=i(e,p)*i(e,a),s=e.getBoundingClientRect();return{x:s.left+t*o,y:s.top+n*o}}function f(t){const n=t.component??`<${t.tag}>`,e=t.component&&t.file?t.file.split("/").pop():null;return[n,e,t.surface].filter(Boolean).join(" \xB7 ")}const m={amber:{border:"border-amber-400",tag:"bg-amber-400 text-amber-950"},sky:{border:"border-sky-400",tag:"bg-sky-400 text-sky-950"}};function h({rect:t,label:n,hint:e,tone:o="amber"}){const s=t.top>(e?u:d);return l("div",{"aria-hidden":!0,className:"pointer-events-none fixed z-[2147483000]",style:{top:t.top,left:t.left,width:t.width,height:t.height},children:[r("div",{className:c("absolute inset-0 border",m[o].border)}),l("span",{className:c("absolute left-0 rounded-sm px-1.5 text-[10px] shadow-sm",e?"flex max-w-[22rem] flex-col py-1 leading-[1.45]":"whitespace-nowrap py-0.5 leading-none",m[o].tag),style:s?{bottom:"100%",marginBottom:3}:{top:"100%",marginTop:3},children:[r("span",{className:c("font-mono",e&&"truncate"),children:n}),e&&r("span",{className:"truncate font-sans opacity-70",children:e})]})]})}function R({target:t}){return t?r(h,{rect:t.rect,label:f(t.node)}):null}export{h as Outline,R as PickHighlight,f as describeNode,N as mapPoint,y as mapRect};
|
package/dist/screen-frame.d.ts
CHANGED
|
@@ -12,16 +12,16 @@ export interface RowExtent {
|
|
|
12
12
|
export declare function rowExtent(widths: readonly number[]): RowExtent;
|
|
13
13
|
export declare function framedWidth(width: number, scale: number): number;
|
|
14
14
|
export declare function columnWidth(width: number, scale: number): number;
|
|
15
|
-
export declare function rowWidth(widths: readonly number[], scale: number): number;
|
|
16
15
|
export declare function fitRows(available: number, rows: readonly RowExtent[]): number;
|
|
17
16
|
export declare function useScale(zoom: Zoom, rows: readonly RowExtent[]): {
|
|
18
17
|
ref: import("react").RefObject<HTMLDivElement | null>;
|
|
19
18
|
scale: number;
|
|
20
19
|
};
|
|
21
|
-
export declare function ScreenFrame({ viewport, scale, label, children, editing, }: {
|
|
20
|
+
export declare function ScreenFrame({ viewport, scale, label, children, editing, bare, }: {
|
|
22
21
|
viewport: Viewport;
|
|
23
22
|
scale: number;
|
|
24
23
|
label?: ReactNode;
|
|
25
24
|
children: ReactNode;
|
|
26
25
|
editing?: boolean;
|
|
26
|
+
bare?: boolean;
|
|
27
27
|
}): import("react").JSX.Element;
|
package/dist/screen-frame.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as
|
|
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};
|
package/dist/screen-toolbar.d.ts
CHANGED
|
@@ -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
|
|
21
|
-
onZoom
|
|
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;
|
package/dist/screen-toolbar.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as c}from"react/jsx-runtime";import{useViewerConfig as
|
|
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/workbench-app.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as n,jsxs as o,Fragment as
|
|
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