@wireweave/core 2.7.1 → 3.0.0-beta.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
@@ -1846,6 +1846,9 @@ function generateLayoutClasses(prefix) {
1846
1846
  padding: 16px;
1847
1847
  display: flex;
1848
1848
  flex-direction: column;
1849
+ /* Default vertical spacing between stacked content blocks.
1850
+ Inline gap from DSL (main gap=N) overrides this. */
1851
+ gap: 16px;
1849
1852
  min-height: 0;
1850
1853
  }
1851
1854
 
@@ -1887,7 +1890,7 @@ function generateLayoutClasses(prefix) {
1887
1890
  }
1888
1891
 
1889
1892
  .${prefix}-sidebar {
1890
- width: 256px;
1893
+ width: 224px;
1891
1894
  border-right: 1px solid var(--${prefix}-border);
1892
1895
  padding: 16px 16px 16px 20px;
1893
1896
  flex-shrink: 0;
@@ -49338,7 +49341,6 @@ function renderDropdown(node, ctx) {
49338
49341
  "data-action": dropdownItem.action
49339
49342
  };
49340
49343
  const interactiveAttrStr = ctx.buildAttrsString(interactiveAttrs);
49341
- const hrefAttr = dropdownItem.href ? ` href="${ctx.escapeHtml(dropdownItem.href)}"` : "";
49342
49344
  const disabledAttr = dropdownItem.disabled ? ' disabled="disabled"' : "";
49343
49345
  if (dropdownItem.href || dropdownItem.navigate) {
49344
49346
  const href = dropdownItem.href || dropdownItem.navigate || "#";
@@ -49442,13 +49444,14 @@ function renderBreadcrumb(node, ctx) {
49442
49444
  ]);
49443
49445
  const styles = ctx.buildCommonStyles(node);
49444
49446
  const styleAttr = styles ? ` style="${styles}"` : "";
49447
+ const separator = `<span class="${ctx.prefix}-breadcrumb-separator" aria-hidden="true">|</span>`;
49445
49448
  const items = node.items.map((item, idx) => {
49446
49449
  const isLast = idx === node.items.length - 1;
49447
49450
  if (typeof item === "string") {
49448
49451
  return isLast ? `<span class="${ctx.prefix}-breadcrumb-item" aria-current="page">${ctx.escapeHtml(item)}</span>` : `<a class="${ctx.prefix}-breadcrumb-item" href="#">${ctx.escapeHtml(item)}</a>`;
49449
49452
  }
49450
49453
  return isLast ? `<span class="${ctx.prefix}-breadcrumb-item" aria-current="page">${ctx.escapeHtml(item.label)}</span>` : `<a class="${ctx.prefix}-breadcrumb-item" href="${item.href || "#"}">${ctx.escapeHtml(item.label)}</a>`;
49451
- }).join(" / ");
49454
+ }).join(separator);
49452
49455
  return `<nav class="${classes}"${styleAttr} aria-label="Breadcrumb">${items}</nav>`;
49453
49456
  }
49454
49457
 
@@ -49855,7 +49858,10 @@ var HtmlRenderer = class extends BaseRenderer {
49855
49858
  const uiContent = this.renderChildren(uiChildren);
49856
49859
  const title = node.title ? `<title>${this.escapeHtml(node.title)}</title>
49857
49860
  ` : "";
49858
- 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);
49859
49865
  let viewportStyle = `position: relative; width: ${viewport.width}px; height: ${viewport.height}px; overflow: hidden`;
49860
49866
  if (this.context.options.background) {
49861
49867
  viewportStyle += `; background: ${this.context.options.background}`;
@@ -50274,8 +50280,99 @@ function createHtmlRenderer(options) {
50274
50280
  return new HtmlRenderer(options);
50275
50281
  }
50276
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
+
50277
50369
  // src/renderer/index.ts
50278
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
+ }
50279
50376
  const renderer = createHtmlRenderer(options);
50280
50377
  return renderer.render(document);
50281
50378
  }
@@ -50311,24 +50408,33 @@ ${html}
50311
50408
  </html>`;
50312
50409
  }
50313
50410
  function renderToSvg(document, options = {}) {
50314
- const firstPage = document.children[0];
50411
+ const isMultiPage = document.children.length > 1;
50315
50412
  let width = options.width ?? 800;
50316
50413
  let height = options.height ?? 600;
50317
- if (firstPage && options.width === void 0 && options.height === void 0) {
50318
- const pageAny = firstPage;
50319
- const explicitW = firstPage.w ?? pageAny.width;
50320
- const explicitH = firstPage.h ?? pageAny.height;
50321
- if (typeof explicitW === "number" && typeof explicitH === "number") {
50322
- width = explicitW;
50323
- height = explicitH;
50324
- } else if (firstPage.viewport !== void 0 || firstPage.device !== void 0) {
50325
- const viewport = resolveViewport(firstPage.viewport, firstPage.device);
50326
- width = viewport.width;
50327
- 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;
50328
50432
  }
50433
+ const result = render(document, { theme: options.theme ?? "light" });
50434
+ html = result.html;
50435
+ css = result.css;
50329
50436
  }
50330
50437
  const background = options.background ?? "#ffffff";
50331
- const { html, css } = render(document, { theme: options.theme ?? "light" });
50332
50438
  const svg = `<?xml version="1.0" encoding="UTF-8"?>
50333
50439
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
50334
50440
  viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">
@@ -50361,11 +50467,15 @@ export {
50361
50467
  generateStyles,
50362
50468
  getIconData,
50363
50469
  getTheme,
50470
+ layoutCanvas,
50364
50471
  lucideIcons,
50365
50472
  render,
50473
+ renderCanvas,
50366
50474
  renderIconSvg,
50475
+ renderPage,
50367
50476
  renderToHtml,
50368
- renderToSvg
50477
+ renderToSvg,
50478
+ resolvePageDimensions
50369
50479
  };
50370
50480
  /**
50371
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.7.1",
3
+ "version": "3.0.0-beta.0",
4
4
  "description": "Core parser and renderer for wireweave",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",