@wireweave/core 2.8.0 → 3.0.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/renderer.js CHANGED
@@ -49341,7 +49341,6 @@ function renderDropdown(node, ctx) {
49341
49341
  "data-action": dropdownItem.action
49342
49342
  };
49343
49343
  const interactiveAttrStr = ctx.buildAttrsString(interactiveAttrs);
49344
- const hrefAttr = dropdownItem.href ? ` href="${ctx.escapeHtml(dropdownItem.href)}"` : "";
49345
49344
  const disabledAttr = dropdownItem.disabled ? ' disabled="disabled"' : "";
49346
49345
  if (dropdownItem.href || dropdownItem.navigate) {
49347
49346
  const href = dropdownItem.href || dropdownItem.navigate || "#";
@@ -49859,7 +49858,10 @@ var HtmlRenderer = class extends BaseRenderer {
49859
49858
  const uiContent = this.renderChildren(uiChildren);
49860
49859
  const title = node.title ? `<title>${this.escapeHtml(node.title)}</title>
49861
49860
  ` : "";
49862
- const commonStyles = this.buildCommonStyles(node);
49861
+ const { x: _canvasX, y: _canvasY, ...nodeWithoutCanvasPos } = node;
49862
+ void _canvasX;
49863
+ void _canvasY;
49864
+ const commonStyles = this.buildCommonStyles(nodeWithoutCanvasPos);
49863
49865
  let viewportStyle = `position: relative; width: ${viewport.width}px; height: ${viewport.height}px; overflow: hidden`;
49864
49866
  if (this.context.options.background) {
49865
49867
  viewportStyle += `; background: ${this.context.options.background}`;
@@ -50278,8 +50280,99 @@ function createHtmlRenderer(options) {
50278
50280
  return new HtmlRenderer(options);
50279
50281
  }
50280
50282
 
50283
+ // src/renderer/page-renderer.ts
50284
+ function renderPage(page, options = {}) {
50285
+ const renderer = new HtmlRenderer(options);
50286
+ const { html, css } = renderer.render({
50287
+ type: "Document",
50288
+ children: [page]
50289
+ });
50290
+ const { width, height } = resolvePageDimensions(page);
50291
+ return { html, css, width, height };
50292
+ }
50293
+ function resolvePageDimensions(page) {
50294
+ const pageAny = page;
50295
+ const explicitW = page.w ?? pageAny.width;
50296
+ const explicitH = page.h ?? pageAny.height;
50297
+ if (typeof explicitW === "number" && typeof explicitH === "number") {
50298
+ return { width: explicitW, height: explicitH };
50299
+ }
50300
+ const viewport = resolveViewport(page.viewport, page.device);
50301
+ return { width: viewport.width, height: viewport.height };
50302
+ }
50303
+
50304
+ // src/renderer/canvas-renderer.ts
50305
+ var DEFAULT_GAP = 64;
50306
+ var DEFAULT_PREFIX = "wf";
50307
+ function layoutCanvas(pages, gap = DEFAULT_GAP) {
50308
+ const placed = [];
50309
+ let cursorX = 0;
50310
+ let maxRight = 0;
50311
+ let maxBottom = 0;
50312
+ for (const page of pages) {
50313
+ const { width, height } = resolvePageDimensions(page);
50314
+ const explicitX = typeof page.x === "number" ? page.x : void 0;
50315
+ const explicitY = typeof page.y === "number" ? page.y : void 0;
50316
+ const x = explicitX ?? cursorX;
50317
+ const y = explicitY ?? 0;
50318
+ placed.push({ page, x, y, w: width, h: height });
50319
+ if (explicitX === void 0) {
50320
+ cursorX = x + width + gap;
50321
+ }
50322
+ maxRight = Math.max(maxRight, x + width);
50323
+ maxBottom = Math.max(maxBottom, y + height);
50324
+ }
50325
+ return { placed, width: maxRight, height: maxBottom };
50326
+ }
50327
+ function renderCanvas(doc, options = {}) {
50328
+ const gap = options.gap ?? DEFAULT_GAP;
50329
+ const prefix = options.classPrefix ?? DEFAULT_PREFIX;
50330
+ const includeStyles = options.includeStyles !== false;
50331
+ const theme = options.theme === "dark" ? darkTheme : defaultTheme;
50332
+ const css = includeStyles ? generateStyles(theme, prefix) : "";
50333
+ if (doc.children.length === 0) {
50334
+ return {
50335
+ html: `<div class="${prefix}-canvas" data-empty="true"></div>`,
50336
+ css,
50337
+ width: 0,
50338
+ height: 0
50339
+ };
50340
+ }
50341
+ const { placed, width, height } = layoutCanvas(doc.children, gap);
50342
+ const pageRenderer = new HtmlRenderer({ ...options, includeStyles: false });
50343
+ const boards = placed.map((p) => {
50344
+ const { html: pageHtml } = pageRenderer.render({
50345
+ type: "Document",
50346
+ children: [p.page]
50347
+ });
50348
+ return wrapBoard(pageHtml, p, prefix);
50349
+ }).join("\n");
50350
+ const canvasStyle = `position: relative; width: ${width}px; height: ${height}px;`;
50351
+ const html = `<div class="${prefix}-canvas" style="${canvasStyle}" data-page-count="${placed.length}">
50352
+ ${boards}
50353
+ </div>`;
50354
+ return { html, css, width, height };
50355
+ }
50356
+ function wrapBoard(pageHtml, placed, prefix) {
50357
+ const { x, y, w, h, page } = placed;
50358
+ const positionStyle = `position: absolute; left: ${x}px; top: ${y}px; width: ${w}px; height: ${h}px;`;
50359
+ const titleAttr = page.title ? ` data-page-title="${escapeAttr(page.title)}"` : "";
50360
+ const dataAttrs = ` data-page-x="${x}" data-page-y="${y}" data-page-w="${w}" data-page-h="${h}"${titleAttr}`;
50361
+ return `<div class="${prefix}-canvas-board" style="${positionStyle}"${dataAttrs}>
50362
+ ${pageHtml}
50363
+ </div>`;
50364
+ }
50365
+ function escapeAttr(s) {
50366
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
50367
+ }
50368
+
50281
50369
  // src/renderer/index.ts
50282
50370
  function render(document, options = {}) {
50371
+ const isMultiPage = document.children.length > 1;
50372
+ if (isMultiPage) {
50373
+ const result = renderCanvas(document, options);
50374
+ return { html: result.html, css: result.css };
50375
+ }
50283
50376
  const renderer = createHtmlRenderer(options);
50284
50377
  return renderer.render(document);
50285
50378
  }
@@ -50315,24 +50408,33 @@ ${html}
50315
50408
  </html>`;
50316
50409
  }
50317
50410
  function renderToSvg(document, options = {}) {
50318
- const firstPage = document.children[0];
50411
+ const isMultiPage = document.children.length > 1;
50319
50412
  let width = options.width ?? 800;
50320
50413
  let height = options.height ?? 600;
50321
- if (firstPage && options.width === void 0 && options.height === void 0) {
50322
- const pageAny = firstPage;
50323
- const explicitW = firstPage.w ?? pageAny.width;
50324
- const explicitH = firstPage.h ?? pageAny.height;
50325
- if (typeof explicitW === "number" && typeof explicitH === "number") {
50326
- width = explicitW;
50327
- height = explicitH;
50328
- } else if (firstPage.viewport !== void 0 || firstPage.device !== void 0) {
50329
- const viewport = resolveViewport(firstPage.viewport, firstPage.device);
50330
- width = viewport.width;
50331
- height = viewport.height;
50414
+ let html;
50415
+ let css;
50416
+ if (isMultiPage) {
50417
+ const canvas = renderCanvas(document, {
50418
+ theme: options.theme ?? "light"
50419
+ });
50420
+ if (options.width === void 0 && options.height === void 0) {
50421
+ width = canvas.width;
50422
+ height = canvas.height;
50423
+ }
50424
+ html = canvas.html;
50425
+ css = canvas.css;
50426
+ } else {
50427
+ const firstPage = document.children[0];
50428
+ if (firstPage && options.width === void 0 && options.height === void 0) {
50429
+ const dims = resolvePageDimensions(firstPage);
50430
+ width = dims.width;
50431
+ height = dims.height;
50332
50432
  }
50433
+ const result = render(document, { theme: options.theme ?? "light" });
50434
+ html = result.html;
50435
+ css = result.css;
50333
50436
  }
50334
50437
  const background = options.background ?? "#ffffff";
50335
- const { html, css } = render(document, { theme: options.theme ?? "light" });
50336
50438
  const svg = `<?xml version="1.0" encoding="UTF-8"?>
50337
50439
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
50338
50440
  viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">
@@ -50365,11 +50467,15 @@ export {
50365
50467
  generateStyles,
50366
50468
  getIconData,
50367
50469
  getTheme,
50470
+ layoutCanvas,
50368
50471
  lucideIcons,
50369
50472
  render,
50473
+ renderCanvas,
50370
50474
  renderIconSvg,
50475
+ renderPage,
50371
50476
  renderToHtml,
50372
- renderToSvg
50477
+ renderToSvg,
50478
+ resolvePageDimensions
50373
50479
  };
50374
50480
  /**
50375
50481
  * Lucide Icons Data
@@ -110,10 +110,33 @@ interface InteractiveProps {
110
110
  /** Custom action identifier (e.g., "submit", "logout", "delete") */
111
111
  action?: string;
112
112
  }
113
+ /**
114
+ * Wireframe Document — root node, holds zero or more `Page`s.
115
+ *
116
+ * Multi-page semantics:
117
+ * - A document is a *canvas* of pages. `renderPage` consumes one page;
118
+ * `renderCanvas` composes all pages into one canvas (grid auto-layout
119
+ * when no coords are set, absolute positioning when `at(x, y)` is set).
120
+ * - The canvas itself (gap, layout) is a *renderer* concern, not part of
121
+ * the DSL — see `CanvasOptions` in `renderer/types.ts`. Chrome / grid /
122
+ * pan-zoom are host concerns and live entirely outside the renderer.
123
+ */
113
124
  interface WireframeDocument extends BaseNode {
114
125
  type: 'Document';
115
126
  children: PageNode[];
116
127
  }
128
+ /**
129
+ * Page — a single fixed-size board (one wireframe screen).
130
+ *
131
+ * Note on `x` / `y` (inherited from `PositionProps` via `CommonProps`):
132
+ * for `Page` these are **canvas coordinates** — the position of this page's
133
+ * top-left corner on the multi-page canvas, in pixels. They are written in
134
+ * DSL as `page "Login" at(0, 0) { ... }`. Pages without `at(...)` get
135
+ * auto-grid placement at canvas-render time.
136
+ *
137
+ * For non-page nodes the same `x` / `y` mean position inside a `Relative`
138
+ * parent — context disambiguates.
139
+ */
117
140
  interface PageNode extends BaseNode, CommonProps {
118
141
  type: 'Page';
119
142
  title?: string | null;
@@ -110,10 +110,33 @@ interface InteractiveProps {
110
110
  /** Custom action identifier (e.g., "submit", "logout", "delete") */
111
111
  action?: string;
112
112
  }
113
+ /**
114
+ * Wireframe Document — root node, holds zero or more `Page`s.
115
+ *
116
+ * Multi-page semantics:
117
+ * - A document is a *canvas* of pages. `renderPage` consumes one page;
118
+ * `renderCanvas` composes all pages into one canvas (grid auto-layout
119
+ * when no coords are set, absolute positioning when `at(x, y)` is set).
120
+ * - The canvas itself (gap, layout) is a *renderer* concern, not part of
121
+ * the DSL — see `CanvasOptions` in `renderer/types.ts`. Chrome / grid /
122
+ * pan-zoom are host concerns and live entirely outside the renderer.
123
+ */
113
124
  interface WireframeDocument extends BaseNode {
114
125
  type: 'Document';
115
126
  children: PageNode[];
116
127
  }
128
+ /**
129
+ * Page — a single fixed-size board (one wireframe screen).
130
+ *
131
+ * Note on `x` / `y` (inherited from `PositionProps` via `CommonProps`):
132
+ * for `Page` these are **canvas coordinates** — the position of this page's
133
+ * top-left corner on the multi-page canvas, in pixels. They are written in
134
+ * DSL as `page "Login" at(0, 0) { ... }`. Pages without `at(...)` get
135
+ * auto-grid placement at canvas-render time.
136
+ *
137
+ * For non-page nodes the same `x` / `y` mean position inside a `Relative`
138
+ * parent — context disambiguates.
139
+ */
117
140
  interface PageNode extends BaseNode, CommonProps {
118
141
  type: 'Page';
119
142
  title?: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wireweave/core",
3
- "version": "2.8.0",
3
+ "version": "3.0.0",
4
4
  "description": "Core parser and renderer for wireweave",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",