Files
archify-vscode-ext/vendor/archify/renderers/shared/desktop-readability.mjs
T
oleg-lukasonokandClaude Opus 5.5 d56e784e97 Archify Diagram Viewer 0.1.0
VS Code extension that previews Archify diagrams from their JSON sources
(live, as you type) and opens rendered Archify HTML in a viewer tab.
Bundles the Archify 3.0.1 renderer and runs it on VS Code's Node runtime.
Adds validation diagnostics, JSON schema help, source-link navigation,
export saving, render-to-file and open-in-browser commands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 12:40:55 +03:00

133 lines
6.7 KiB
JavaScript

export const DESKTOP_READABILITY_VIEWPORT = Object.freeze({ width: 1440, height: 900 });
export const DESKTOP_READER_MIN_WIDTH = 960;
export const DESKTOP_READER_HORIZONTAL_CHROME = 30;
export const DESKTOP_READER_DIAGRAM_WIDTH = DESKTOP_READER_MIN_WIDTH - DESKTOP_READER_HORIZONTAL_CHROME;
export const MIN_PROJECTED_NODE_TEXT_PX = 6;
export const DECLARED_WIDE_READER_CONTRACT = 'declared-wide-v1';
export const DECLARED_WIDE_READER_RATIO = 1.55;
export const DECLARED_WIDE_READER_MAX_WIDTH = 1920;
export const DECLARED_WIDE_REFERENCE_BODY_HORIZONTAL_PX = 64;
export const DECLARED_WIDE_REFERENCE_DIAGRAM_HORIZONTAL_PX = 30;
export function projectedNodeTextPx(sourceFontPx, viewBoxWidth, diagramWidth = DESKTOP_READER_DIAGRAM_WIDTH) {
if (![sourceFontPx, viewBoxWidth, diagramWidth].every(Number.isFinite) || viewBoxWidth <= 0 || diagramWidth <= 0) {
return Number.NaN;
}
return sourceFontPx * Math.min(1, diagramWidth / viewBoxWidth);
}
export function minimumReadableSourceTextPx(
viewBoxWidth,
diagramWidth = DESKTOP_READER_DIAGRAM_WIDTH,
minimumProjectedPx = MIN_PROJECTED_NODE_TEXT_PX,
) {
if (![viewBoxWidth, diagramWidth, minimumProjectedPx].every(Number.isFinite)
|| viewBoxWidth <= 0
|| diagramWidth <= 0
|| minimumProjectedPx <= 0) {
return Number.NaN;
}
return minimumProjectedPx / Math.min(1, diagramWidth / viewBoxWidth);
}
// This is deliberately separate from the legacy 930px projection. Architecture
// boundary convergence depends on that legacy default, while only a recognized
// v2 wide Reader may use this declared-width proof.
export function declaredWideReadabilityBudget({
viewBoxWidth,
viewBoxHeight,
minimumSourceTextPx,
requestedMinimumTextPx,
viewportWidth = DESKTOP_READABILITY_VIEWPORT.width,
bodyHorizontalPx = DECLARED_WIDE_REFERENCE_BODY_HORIZONTAL_PX,
diagramHorizontalPx = DECLARED_WIDE_REFERENCE_DIAGRAM_HORIZONTAL_PX,
minimumReaderWidth = DESKTOP_READER_MIN_WIDTH,
maximumReaderWidth = DECLARED_WIDE_READER_MAX_WIDTH,
} = {}) {
const values = [
viewBoxWidth, viewBoxHeight, minimumSourceTextPx, requestedMinimumTextPx,
viewportWidth, bodyHorizontalPx, diagramHorizontalPx, minimumReaderWidth, maximumReaderWidth,
];
if (!values.every(Number.isFinite) || viewBoxWidth <= 0 || viewBoxHeight <= 0
|| minimumSourceTextPx <= 0 || requestedMinimumTextPx <= 0 || viewportWidth <= 0
|| bodyHorizontalPx < 0 || diagramHorizontalPx < 0 || minimumReaderWidth <= 0
|| maximumReaderWidth < minimumReaderWidth || viewBoxWidth / viewBoxHeight < DECLARED_WIDE_READER_RATIO) {
return null;
}
const requestedTargetPx = Math.max(MIN_PROJECTED_NODE_TEXT_PX, requestedMinimumTextPx);
const requestedScale = Math.min(1, requestedTargetPx / minimumSourceTextPx);
const desiredReaderWidth = Math.max(minimumReaderWidth, viewBoxWidth * requestedScale + diagramHorizontalPx);
const viewportCap = Math.max(0, viewportWidth - bodyHorizontalPx);
const cap = Math.min(maximumReaderWidth, viewportCap);
const actualReaderWidth = Math.min(desiredReaderWidth, cap);
const guaranteedSvgWidth = Math.max(0, actualReaderWidth - diagramHorizontalPx);
const projectedMinimumTextPx = projectedNodeTextPx(minimumSourceTextPx, viewBoxWidth, guaranteedSvgWidth);
const limit = actualReaderWidth < desiredReaderWidth
? (viewportCap <= maximumReaderWidth ? 'viewport-cap' : 'reader-cap')
: 'source-size';
return {
requestedTargetPx,
requestedScale,
desiredReaderWidth,
viewportCap,
maximumReaderWidth,
actualReaderWidth,
guaranteedSvgWidth,
projectedMinimumTextPx,
hardFloorPx: MIN_PROJECTED_NODE_TEXT_PX,
hardFloorMet: projectedMinimumTextPx >= MIN_PROJECTED_NODE_TEXT_PX,
requestedTargetMet: projectedMinimumTextPx >= requestedMinimumTextPx,
limit,
};
}
// Vertical chrome that always stacks with the SVG at the 1440x900 desktop
// viewport, measured from the delivered Viewer with the shortest one-line
// header and no cards: body padding 12, header 39, diagram padding/border 75.
// Cards are excluded so the prediction stays a lower bound.
export const DESKTOP_FIXED_VERTICAL_CHROME_PX = Object.freeze({ body: 12, header: 39, diagram: 75 });
// A canvas the Reader can neither narrow (viewBox ratio below the wide
// threshold) nor scroll readably (no intrinsic-height fit) renders at the full
// reader width, so its page height is a function of the viewBox alone. Returns
// null when the Reader has a way to fit the page; otherwise the certain
// overflow at 1440x900 before any cards are counted.
export function predictedFixedWidthOverflow({
viewBoxWidth,
viewBoxHeight,
readerFit,
diagramType,
viewport = DESKTOP_READABILITY_VIEWPORT,
bodyHorizontalPx = DECLARED_WIDE_REFERENCE_BODY_HORIZONTAL_PX,
diagramHorizontalPx = DECLARED_WIDE_REFERENCE_DIAGRAM_HORIZONTAL_PX,
chrome = DESKTOP_FIXED_VERTICAL_CHROME_PX,
} = {}) {
if (![viewBoxWidth, viewBoxHeight].every(Number.isFinite) || viewBoxWidth <= 0 || viewBoxHeight <= 0) return null;
const ratio = viewBoxWidth / viewBoxHeight;
if (readerFit === 'intrinsic-height'
|| (readerFit === 'authored-height' && diagramType === 'architecture')
|| ratio >= DECLARED_WIDE_READER_RATIO) return null;
const svgWidthPx = viewport.width - bodyHorizontalPx - diagramHorizontalPx;
const svgHeightPx = Math.round(svgWidthPx * viewBoxHeight / viewBoxWidth);
const fixedChromePx = chrome.body + chrome.header + chrome.diagram;
const pageHeightPx = svgHeightPx + fixedChromePx;
if (pageHeightPx <= viewport.height) return null;
return {
viewportWidth: viewport.width,
viewportHeight: viewport.height,
ratio: Math.round(ratio * 100) / 100,
wideRatio: DECLARED_WIDE_READER_RATIO,
svgWidthPx,
svgHeightPx,
fixedChromePx,
pageHeightPx,
overflowPx: pageHeightPx - viewport.height,
};
}
export function describeFixedWidthOverflow(issue) {
const maximumViewBoxHeight = Math.floor(issue.viewBoxWidth / issue.wideRatio);
const wideViewBoxWidth = Math.ceil(issue.viewBoxHeight * issue.wideRatio);
return `Preserve every node, relationship, and label. This ${issue.viewBoxWidth}x${issue.viewBoxHeight} canvas (ratio ${issue.ratio}) declares no intrinsic-height fit and is below the ${issue.wideRatio} wide ratio, so the desktop Reader can neither narrow it nor accept vertical scroll: it renders ${issue.svgHeightPx}px tall at the full ${issue.svgWidthPx}px width and the page reaches ${issue.pageHeightPx}px before cards against ${issue.viewportHeight}px, a certain visual-check failure. Either compact vertical spacing so meta.viewBox height is at most ${maximumViewBoxHeight} at this width, or spread content sideways so the width is at least ${wideViewBoxWidth} at this height; for architecture, omitting meta.viewBox lets the renderer size the canvas and declare the fit.`;
}