### Install Project Dependencies Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Run this command once to install all necessary project dependencies. ```sh bun install ``` -------------------------------- ### Start Local Development Server Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Starts a local page server for development. Access it at http://localhost:3000. ```sh bun start ``` -------------------------------- ### Install Pretext via npm Source: https://github.com/chenglou/pretext/blob/main/README.md Use npm to install the Pretext library. This is the first step before using its functionalities in your project. ```sh npm install @chenglou/pretext ``` -------------------------------- ### Start Local Development Server (Windows Fallback) Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md A Windows-friendly fallback to start the local development server, without automatic port cleanup. ```sh bun run start:windows ``` -------------------------------- ### Run Dashboard Status Source: https://github.com/chenglou/pretext/blob/main/corpora/STATUS.md Executes the status dashboard command. No specific setup is required beyond having the bun environment configured. ```sh bun run status-dashboard ``` -------------------------------- ### Layout Next Line Iteratively Source: https://github.com/chenglou/pretext/blob/main/README.md Use `layoutNextLine` as an iterator-like API to lay out lines one by one, starting from a given cursor. Pass the previous line's `end` cursor as the next `start`. ```typescript layoutNextLine(prepared: PreparedTextWithSegments, start: LayoutCursor, maxWidth: number): LayoutLine | null ``` -------------------------------- ### Build Static Demo Site Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Builds the static demo site and outputs it into the 'site/' directory. ```sh bun run site:build ``` -------------------------------- ### Prepare with Whitespace Options Source: https://github.com/chenglou/pretext/blob/main/README.md Demonstrates using the `prepare` function with the `whiteSpace: 'pre-wrap'` option to preserve ordinary spaces, tabs, and newlines, similar to CSS `pre-wrap` behavior. ```APIDOC ## Prepare with Whitespace Options ### Description Prepares text for layout while preserving whitespace characters like spaces, tabs, and newlines, mimicking CSS `white-space: pre-wrap`. ### Method ```ts const prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' }) const { height } = layout(prepared, textareaWidth, 20) ``` ### Parameters #### `prepare(text: string, font: string, options: { whiteSpace: 'pre-wrap' })` - `text` (string): The input text from a textarea. - `font` (string): The font style and size. - `options` (object): Configuration object. - `whiteSpace` (string): Set to 'pre-wrap' to preserve whitespace. #### `layout(prepared: PreparedText, width: number, lineHeight: number)` - `prepared` (PreparedText): The handle from `prepare`. - `width` (number): The container width. - `lineHeight` (number): The line height. ``` -------------------------------- ### Sweep Corpus Data Source: https://github.com/chenglou/pretext/blob/main/corpora/STATUS.md Executes a sweep operation on corpus data for a given ID, specifying a start, end, and step. This is used for comprehensive data analysis over a range. ```sh bun run corpus-sweep --id=zh-guxiang --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=ja-kumo-no-ito --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=ja-rashomon --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=zh-zhufu --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=my-cunning-heron-teacher --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=my-bad-deeds-return-to-you-teacher --start=300 --end=900 --step=10 ``` ```sh bun run corpus-sweep --id=ur-chughd --start=300 --end=900 --step=10 ``` -------------------------------- ### Run Pre-wrap Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Performs a compact batched browser oracle check for '{ whiteSpace: 'pre-wrap' }'. ```sh bun run pre-wrap-check ``` -------------------------------- ### Run Benchmark Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Performs a benchmark check against Chrome. Defaults to the median of 3 full page runs. Use '--runs=1' for a quick local check. ```sh bun run benchmark-check ``` -------------------------------- ### Measure Line Count and Max Width Source: https://github.com/chenglou/pretext/blob/main/README.md Use `measureLineStats` to efficiently get the number of lines and the width of the widest line for a given text and maximum width, avoiding string allocations. ```typescript measureLineStats(prepared: PreparedTextWithSegments, maxWidth: number): { lineCount: number, maxLineWidth: number } ``` -------------------------------- ### Run Corpus Taxonomy Tool Source: https://github.com/chenglou/pretext/blob/main/corpora/TAXONOMY.md Use this command to batch browser diagnostics into a steering summary for identifying text layout issues. ```sh bun run corpus-taxonomy --id=ja-rashomon 330 450 ``` -------------------------------- ### Sweep Corpus Data for Safari Source: https://github.com/chenglou/pretext/blob/main/corpora/STATUS.md Performs a sweep operation on corpus data for Safari, including all data, with specified start, end, and step parameters. The output is directed to a specific JSON file. ```sh bun run corpus-sweep --browser=safari --all --start=300 --end=900 --step=10 --output=corpora/safari-step10.json ``` -------------------------------- ### Layout Lines at Fixed Width Source: https://github.com/chenglou/pretext/blob/main/README.md Use `layoutWithLines` to get all lines of text pre-formatted to a specific maximum width and line height. This is useful for rendering text in fixed-layout environments like canvas. ```typescript import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext' const prepared = prepareWithSegments('AGI 春天到了. بدأت الرحلة 🚀', '18px "Helvetica Neue"') const { lines } = layoutWithLines(prepared, 320, 26) // 320px max width, 26px line height for (let i = 0; i < lines.length; i++) ctx.fillText(lines[i].text, 0, i * 26) ``` -------------------------------- ### Run Safari Benchmark Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Performs a benchmark check against the Safari browser. ```sh bun run benchmark-check:safari ``` -------------------------------- ### Prepare and Layout Text Source: https://github.com/chenglou/pretext/blob/main/README.md This snippet demonstrates how to use the `prepare` and `layout` functions to measure text height and line count without touching the DOM. `prepare` normalizes text and measures segments, returning an opaque handle. `layout` then uses this handle for fast, arithmetic-based calculations. ```APIDOC ## Prepare and Layout Text ### Description Measures text height and line count without DOM interaction. `prepare` precomputes text segments, and `layout` performs fast arithmetic calculations. ### Method ```ts import { prepare, layout } from '@chenglou/pretext' const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀‎', '16px Inter') const { height, lineCount } = layout(prepared, 320, 20) ``` ### Parameters #### `prepare(text: string, font: string, options?: PrepareOptions)` - `text` (string): The input text to measure. - `font` (string): The font style and size (e.g., '16px Inter'). - `options` (object, optional): Configuration options. - `whiteSpace` (string): Controls whitespace handling (e.g., 'pre-wrap'). - `wordBreak` (string): Controls word breaking (e.g., 'keep-all'). - `letterSpacing` (number): Adjusts letter spacing in pixels. #### `layout(prepared: PreparedText, width: number, lineHeight: number)` - `prepared` (PreparedText): The opaque handle returned by `prepare`. - `width` (number): The available width for the text layout. - `lineHeight` (number): The height of a single line. ### Response #### Success Response - `height` (number): The calculated height of the text. - `lineCount` (number): The number of lines the text occupies. ``` -------------------------------- ### Layout Next Rich Inline Line Range Source: https://github.com/chenglou/pretext/blob/main/README.md Use `layoutNextRichInlineLineRange` to stream rich-text inline flow line by line without building fragment strings. It accepts an optional starting cursor. ```typescript layoutNextRichInlineLineRange(prepared: PreparedRichInline, maxWidth: number, start?: RichInlineCursor): RichInlineLineRange | null ``` -------------------------------- ### Run Unit Tests Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Executes the suite of small, durable invariant tests. ```sh bun test ``` -------------------------------- ### Pretext Shrinkwrap Implementation (JavaScript) Source: https://github.com/chenglou/pretext/blob/main/pages/demos/bubbles.html This JavaScript code demonstrates how to use Pretext's `walkLineRanges()` to find the tightest width for multiline text, minimizing wasted space. It measures text without relying on DOM reflows, offering a pure arithmetic solution for optimal layout. ```javascript (function applyInitialShrinkwrapRender() { const root = document.documentElement const slider = document.getElementById('slider') const shrinkWaste = document.getElementById('shrink-waste') const cssChat = document.getElementById('chat-css') const shrinkChat = document.getElementById('chat-shrink') if (!(slider instanceof HTMLInputElement) || !(shrinkWaste instanceof HTMLSpanElement) || !(cssChat instanceof HTMLDivElement) || !(shrinkChat instanceof HTMLDivElement)) { return } const datasetWidth = root.dataset['bubblesChatWidth'] const chatWidth = datasetWidth === undefined ? Number.parseInt(slider.value, 10) : Number.parseInt(datasetWidth, 10) const bubbleMaxWidth = Math.floor(chatWidth * 0.8) const shrinkNodes = Array.from(shrinkChat.querySelectorAll('.msg')) const cssNodes = Array.from(cssChat.querySelectorAll('.msg')) const firstCssNode = cssNodes[0] const firstCssStyle = firstCssNode instanceof HTMLDivElement ? getComputedStyle(firstCssNode) : null const lineHeight = firstCssStyle === null ? 20 : Number.parseFloat(firstCssStyle.lineHeight) const verticalPadding = firstCssStyle === null ? 16 : Number.parseFloat(firstCssStyle.paddingTop) + Number.parseFloat(firstCssStyle.paddingBottom) function getLineCount(node, height) { return Math.max(1, Math.round((height - verticalPadding) / lineHeight)) } function findTightStartupWidth(shrinkNode, cssWidth, initialLineCount) { let lo = 1 let hi = Math.max(1, cssWidth) while (lo < hi) { const mid = Math.floor((lo + hi) / 2) shrinkNode.style.width = `${mid}px` const midLineCount = getLineCount(shrinkNode, shrinkNode.getBoundingClientRect().height) if (midLineCount <= initialLineCount) { hi = mid } else { lo = mid + 1 } } shrinkNode.style.width = `${lo}px` } root.style.setProperty('--chat-width', `${chatWidth}px`) root.style.setProperty('--bubble-max-width', `${bubbleMaxWidth}px`) for (let index = 0; index < shrinkNodes.length; index++) { const shrinkNode = shrinkNodes[index] const cssNode = cssNodes[index] if (!(shrinkNode instanceof HTMLDivElement) || !(cssNode instanceof HTMLDivElement)) continue cssNode.style.maxWidth = `${bubbleMaxWidth}px` cssNode.style.width = 'fit-content' const cssBox = cssNode.getBoundingClientRect() const cssWidth = Math.ceil(cssBox.width) const initialLineCount = getLineCount(cssNode, cssBox.height) shrinkNode.style.maxWidth = `${bubbleMaxWidth}px` findTightStartupWidth(shrinkNode, cssWidth, initialLineCount) } shrinkWaste.textContent = '0' })() ``` -------------------------------- ### Define CSS Variables and Global Layout Styles Source: https://github.com/chenglou/pretext/blob/main/pages/demos/dynamic-layout.html Sets up the color palette and base box-sizing for the application. It ensures consistent typography and background colors across the entire document. ```css :root { --paper: #f6f0e6; --ink: #11100d; --muted: #4f463b; --accent: #d97757; } * { box-sizing: border-box; } html, body { margin: 0; height: 100%; background: var(--paper); color: var(--ink); } ``` -------------------------------- ### Run Keep-all Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Performs a compact batched browser oracle check for '{ wordBreak: 'keep-all' }', including mixed-script no-space canaries. ```sh bun run keep-all-check ``` -------------------------------- ### Measure Text Height Without DOM Source: https://github.com/chenglou/pretext/blob/main/README.md Prepare text for layout and then calculate its height and line count for a given width. Avoid rerunning `prepare` for the same text and configurations. ```ts import { prepare, layout } from '@chenglou/pretext' const prepared = prepare('AGI 春天到了. بدأت الرحلة 🚀‎', '16px Inter') const { height, lineCount } = layout(prepared, 320, 20) // pure arithmetic. No DOM layout & reflow! ``` -------------------------------- ### Run Safari Probe Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md A smaller Safari browser probe/diagnostic entrypoint. On a first-break mismatch, probe output includes a small break trace. ```sh bun run probe-check:safari ``` -------------------------------- ### Run Probe Check Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md A smaller browser probe/diagnostic entrypoint. On a first-break mismatch, probe output includes a small break trace. ```sh bun run probe-check ``` -------------------------------- ### Run Typecheck, Lint, and Dead-code Scan Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Executes type checking, linting, and dead-code scanning using Knip. ```sh bun run check ``` -------------------------------- ### High-Level Layout with Lines Source: https://github.com/chenglou/pretext/blob/main/README.md Use `layoutWithLines` for manual layout needs, accepting a fixed maximum width for all lines. It returns height, line count, and detailed line information. ```typescript layoutWithLines(prepared: PreparedTextWithSegments, maxWidth: number, lineHeight: number): { height: number, lineCount: number, lines: LayoutLine[] } ``` -------------------------------- ### Apply Initial Bubble Controls (JavaScript) Source: https://github.com/chenglou/pretext/blob/main/pages/demos/bubbles.html This JavaScript function initializes UI controls for chat bubble width, linking a slider input to display the current chat width. It reads values previously set by syncInitialBubbleGeometry. Dependencies: HTML elements with IDs 'slider' and 'val', and dataset attributes on documentElement. ```javascript (function applyInitialBubbleControls() { const slider = document.getElementById('slider'); const label = document.getElementById('val'); if (!(slider instanceof HTMLInputElement)) return; if (!(label instanceof HTMLSpanElement)) return; const root = document.documentElement; const chatWidth = root.dataset['bubblesChatWidth']; const maxWidth = root.dataset['bubblesMaxWidth']; if (chatWidth === undefined || maxWidth === undefined) return; slider.max = maxWidth; slider.value = chatWidth; label.textContent = `${chatWidth}px`; })() ``` -------------------------------- ### Prepare Text with Pre-wrap Whitespace Source: https://github.com/chenglou/pretext/blob/main/README.md When using `prepare`, pass `{ whiteSpace: 'pre-wrap' }` to preserve ordinary spaces, tabs, and newlines, similar to CSS `pre-wrap` behavior. ```ts const prepared = prepare(textareaValue, '16px Inter', { whiteSpace: 'pre-wrap' }) const { height } = layout(prepared, textareaWidth, 20) ``` -------------------------------- ### Prepare Text for Layout Source: https://github.com/chenglou/pretext/blob/main/README.md Use `prepare` for a one-time text analysis and measurement pass. Ensure `font` and `letterSpacing` match your CSS for accurate results. This function returns an opaque value for use with `layout`. ```typescript prepare(text: string, font: string, options?: { whiteSpace?: 'normal' | 'pre-wrap', wordBreak?: 'normal' | 'keep-all', letterSpacing?: number }): PreparedText ``` -------------------------------- ### Build Package for Distribution Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Generates the 'dist/' directory containing the published ESM package. ```sh bun run build:package ``` -------------------------------- ### Package Smoke Test Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Creates a tarball of the package and verifies its usability with temporary JavaScript and TypeScript consumers. ```sh bun run package-smoke-test ``` -------------------------------- ### Calculate Initial Bubble Geometry (JavaScript) Source: https://github.com/chenglou/pretext/blob/main/pages/demos/bubbles.html This JavaScript function calculates the optimal width for chat bubbles based on viewport dimensions, container constraints, and predefined margins and gaps. It sets CSS variables for dynamic styling. Dependencies: None, uses browser DOM APIs. ```javascript (function syncInitialBubbleGeometry() { const root = document.documentElement; const minWidth = 220; const requestedWidth = 340; const pageMaxWidth = 1080; const desktopPageMargin = 32; const mobilePageMargin = 20; const gridGap = 16; const panelPaddingX = 36; const viewportWidth = root.clientWidth; const pageWidth = Math.min(pageMaxWidth, viewportWidth - (viewportWidth <= 760 ? mobilePageMargin : desktopPageMargin)); const columnWidth = viewportWidth <= 760 ? pageWidth : (pageWidth - gridGap) / 2; const panelContentWidth = Math.max(1, Math.floor(columnWidth - panelPaddingX)); const maxWidth = Math.max(minWidth, panelContentWidth); const chatWidth = Math.min(requestedWidth, maxWidth); const bubbleMaxWidth = Math.floor(chatWidth * 0.8); root.dataset['bubblesChatWidth'] = String(chatWidth); root.dataset['bubblesMaxWidth'] = String(maxWidth); root.style.setProperty('--chat-width', `${chatWidth}px`); root.style.setProperty('--bubble-max-width', `${bubbleMaxWidth}px`); })() ``` -------------------------------- ### Run Corpus Under Alternate Fonts Source: https://github.com/chenglou/pretext/blob/main/DEVELOPMENT.md Analyzes the corpus under alternate font configurations. ```sh bun run corpus-font-matrix ```