From: Thomas Walker Lynch Date: Fri, 7 Aug 2026 06:23:20 +0000 (+0000) Subject: clean page splitting and clean diciotnary grid layout X-Git-Url: https://git.reasoningtechnology.com/%27%20%20%20resolved_path%20%20%20%27?a=commitdiff_plain;h=81cd2c3b52120464a01519f53ee52015a684ca46;p=RT-Style clean page splitting and clean diciotnary grid layout --- diff --git a/developer/authored/Manuscript.copy/Document/design.html b/developer/authored/Manuscript.copy/Document/design.html index 4eb0861..0509b97 100644 --- a/developer/authored/Manuscript.copy/Document/design.html +++ b/developer/authored/Manuscript.copy/Document/design.html @@ -831,6 +831,32 @@ + + Composing labels +

+ A label in a grid involves two decisions that are easily mistaken for one. How the lines of a label sit relative to each other is a property of the label. Where that block of lines sits within its cell is a property of the placement. Answering both with a single alignment on the cell gives ragged text pressed against an edge, which reads as though the words were spilled into the corner rather than set there. +

+

+ The label is therefore composed as an object in its own right, in a box sized to its content and capped at the width of the cell, with its lines set flush left because that is how prose is read. That box is then placed: against the data for a row heading, so the label and the thing it labels meet; centred over the column for a column heading or a datum. It is the convention of a drawing, where a text label is made to look right on its own and then attached. +

+

+ Line lengths within the label are evened so that no line is left holding a single stranded word, which is what makes a narrow heading column look broken. Where a browser does not implement the evening, the text still wraps and only the evenness is lost. +

+
+ + + Widows and orphans +

+ The engine carries no widow or orphan control for prose. A short closing paragraph is never held back from a page boundary, and a paragraph is never split, so a page break falls wherever the measurements put it. +

+

+ The single exception is headings. When a page is emitted, trailing headings are carried forward with the content they introduce, so a heading is never stranded at the foot of a page above content that has moved on without it. +

+

+ This is worth stating because the absence is easily mistaken for its presence. A page whose last paragraph would have been widowed had it split, and which instead runs long, looks like a deliberate accommodation and is not one. Before crediting such a page to judgement the engine does not exercise, check the measurements: a page running long is more often a height reported short than a boundary chosen well. +

+
+ Debugging

@@ -936,6 +962,9 @@ Splitting. Zero height children — make tags, snapshots, whitespace — are not progress. A fragment holding several of them and nothing else yields a page carrying only its own page number. + + Any measuring container. A page carries padding, so its content box is narrower than the article. Measuring at the article's width lets text wrap less than it will when rendered, reporting every height short, and pages then run long with nothing to indicate that anything went wrong. + Anything measured in a container rather than in place. A height that follows the width available is not a property of the content, and it changes when a developer panel is docked or a window resized. Resolve such widths once and record them, so that probe and fragment lay out as the original did. diff --git a/developer/authored/Manuscript.copy/Document/manual.html b/developer/authored/Manuscript.copy/Document/manual.html deleted file mode 100644 index 3ff0311..0000000 --- a/developer/authored/Manuscript.copy/Document/manual.html +++ /dev/null @@ -1,382 +0,0 @@ - - - - - RT Manuscript: Reference Manual - - - - - - - - - - - -

- A semantic JavaScript engine for intercepting and compiling raw DOM data graphs into formatted technical manuscripts. -

- - - - - Philosophical Approach - -

- An RT-Style document follows an abstraction hierarchy. At the highest level there is theme. The current working theme is inverse_wheat. You are probably looking at it now. There are a couple of others that might or might not be working. Theme colors are set by the role played in the document; they do not refer to any DOM elements. The next layer down the abstraction stack is layout. The current working layout is article_tech_ref. The purpose of layout is to set the general size and placement of elements. article_tech_ref assumes the text will be laid out across pages and displayed in a browser. It allows page length to be flexible so that some elements can appear without breaks in them. Other layouts could do things differently. Then the RT semantic elements are placed in the layout. An RT semantic element is named by the role it plays in the document, such as title page, section, code, math, etc. -

- -

- There is no CSS file to edit. CSS is a miniature language for static styling. However, it lacks loops or conditionals, and even variables are an awkward addition. In RT-Style, style is painted onto elements from JavaScript functions. Each element has an apply_style function found at the top of its source code. It is not expected that an author of a document will edit these; rather, their values are computed dynamically from the theme settings. If something isn't working out in the presentation, this is considered to be a design problem, not a reason to be modifying style. -

- -

- RT-Style uses the middle dot as a namespace separator in tags. This is the middle dot of typography, so it is supported as a text character in modern systems. On macOS: Option + Shift + 9. On Windows: Hold Alt and type 0183 on the numeric keypad. Linux: Compose Key followed by . and -. -

- -

- As a quick review of terminology, note that content is found between the opening HTML tag and the closing HTML tag. E.g. <RT·noop>This is content.</RT·noop>. Whereas an attribute is a name-value pair found within an opening HTML tag. E.g. <RT·noop name="value">. Here the attribute name is name and the attribute value is value. -

- -

- For RT-Style to work, the document will need to include a file that points it at the library, as discussed in the section on page . -

- -

- For examples of using RT-Style, take a look at the source code for this document, and look in the project test directory. -

- -
- - - Manuscript Types -

- These are the top-level semantic environments. They establish the macro-boundaries that enforce the rendering constraints for all scoped child content. -

- - - - Standard technical document architecture designed for continuous digital reading. - - - Strict print layout constraint designed to mirror standard physical paper dimensions. - - - Multi-chapter assembly architecture. - - - -

- Implementation Note: The <RT·book> element is a planned extension. We are deliberately delaying its separation from the article layout until the core engine's pagination and state machine logic are fully stabilized. Currently, book mechanics operate by triggering page breaks and section scoping within the standard article stream. -

-
- - - Article Environment -

The standard operational header template for an article instance:

- - - <!DOCTYPE html> - <html lang="en"> - <head> - <meta charset="UTF-8"> - <title>RT Manuscript: Reference Manual</title> - <script src="RT-Manuscript_locator.js"></script> - <script> - window.RT.theme_preference('inverse_wheat'); - window.RT.load('Layout/paginate'); - window.RT.load('Layout/article_tech_ref'); - window.RT.load('Element/theme_selector'); - </script> - </head> - <body> - <RT·theme-selector></RT·theme-selector> - <RT·article> - … - </RT·article> - </body> - </html> - - - - Decorators - - - Semantic anchor for technical terminology. Formats the initial occurrence and registers the element ID for index generation. Authors should decorate all occurrences of the term so that they can be indexed.
-
- - Semantic anchor for a novel term coined within the current document boundaries. Again all instances should be decorated.
-
-
-
- - - Layout environments -

- Context-aware formatting blocks. Valid inline or as distinct segments. -

- - - Scoped nestable section marker. Each nesting level is styled differently. - - - Gives a name to the immediately container. Evaluated by sections to set the section heading, by relations to name a relation, and by matrices to name vectors, etc. Multiple instances are concatenated with newlines. - - - Code span or pre-formatted code block. If opening and closing tags are on the same line, content is considered to be inline span code. Otherwise, the content is considered to be a block of code. The left edge of the code is taken to the left edge of the tags. If code is found to the left of the tags, then the left edge is that of the source file. - - - Injects mathematical layout evaluation via the MathJax rendering engine. If opening and closing tags are on the same line, contents are considered to be inline span math. Otherwise, the contents is considered to be a block of math. - - -
- - - Grid environments -

- These elements map semantic data into an internal Cartesian grid state prior to CSS projection, isolating the markup from physical layout mechanics. -

- - - Base Cartesian coordinate grid. Projects child <RT·e> elements based on explicitly defined x and y index geometries.
-
-
Attributes:
- model: Projection strategy. "html-grid-direct" or "html-grid-transpose". (Default: "html-grid-direct")
- major: Carriage return trigger direction. "x" or "y". (Default: "x")
-
- Child Element: <RT·e>
- x: Target x-axis index or index range (e.g., "0-2", where 2 is the maximum index into an array regardless of data type). Default when no y value, is to auto increment, default with a y value is to reset to 0.
- y: Target y-axis index or index range. Default is to keep the prior value.
- type: Decoration type. "data", "x-label", "y-label", or "name". (Default: "data") -
-
- - Two-column associative layout mapping explicitly declared keys to their scoped definitions.
-
-
Attributes:
- key: Header text for the identifier column.
- definition: Header text for the data payload column.
-
- Child Element: <RT·entry>
- key: The term being defined. The scoped HTML of the entry forms the definition value. -
-
- - Relational algebra layout formatter. Enforces multi-line text wrapping limits, calculates row distinction boundaries, and restricts column headers to the tuple scope.
-
-
Attributes:
- layout-intention: "row-tuple" or "column-tuple". (Default: "row-tuple")
-
- Child Elements:
- <RT·tuple-meta>: Describes the components rather than providing them. E.g.<RT·label> provides a component label.
- <RT·tuple>: Container for components. Each <RT·name> is taken as a first component. If there is more than one <RT·name>, they are concatenated with a newline separator. If any tuple has a <RT·name>, then the name column is reserved. Missing names in the name column are given a visual placeholder value of '•'. Component data values are given in <RT·e> tags, these are taken in the order they occur. -
-
- - Linear algebraic matrix representation. Enforces rigid padding, brackets mathematical delimiters, and suppresses the exterior grid coordinate layout.
-
-
Attributes:
- layout-intention: "row-vector" or "column-vector". (Default: "row-vector")
-
- Child Elements:
- <RT·vector-meta>: Describes the components rather than providing them. E.g. <RT·label> provides a component label.
- <RT·vector>: Container for components. Each <RT·name> is taken as a first component. If there is more than one <RT·name>, they are concatenated with a newline separator. If any vector has a <RT·name>, then the name column is reserved. Missing names in the name column are given a visual placeholder value of '•'. Component scalar values are given in <RT·e> tags, these are taken in the order they occur. -
-
-
-
- - - Generators -

Data-injection nodes. These elements do not wrap content; execution is governed strictly by the attributes provided.

- - - - Constructs the primary document header block.
-
-
Attributes:
- title: (Default: "Untitled Document")
- author: (Default: None)
- date: (Default: None)
- copyright: (Default: None) -
-
- - Compiles an automated Table of Contents by scanning the document's nested <RT·section> hierarchy depth.
-
-
Attributes:
- level: Target section depth index "N" or range "A-B". (Default: 1-6). -
-
- - Generates the numeric list of endnote payloads. Replaces all inline endnote citations with bracketed indices. - -
-
- - - Counters -

- Counters operate as explicit state machines. Initialization requires a unique identifier. Execution evaluates scope depth and increments the state. The layout engine maintains a single internal representation but maps it to multiple visual styles upon request. -

- - - - Initializes the named counter state machine.
-
- -
Attributes:
- - counter: Required state identifier.
- -
style: Single style string, or comma-separated hierarchy defining formats at each nesting depth. The terminal format applies to all deeper nestings. Valid arguments: "NaturalNumber", "CountingNumber", "Roman", "roman", "Alpha", "alpha". The "outline" flag translates to a standard mixed document array. (Default: "NaturalNumber")
- -
on-first-step: The value returned the first time the counter is stepped. The default is style specific: "NaturalNumber"(0), "CountingNumber"(1), "Roman"(I), "roman"(i), "Alpha"(A), "alpha"(a).
- -
separator: String appended between depth sequence levels. (Default: ".")
- -
separator-placement: "embedded" or "embedded-after". (Default: "embedded")
- -
mode: "scoped" (content embedded between count boundaries receives the parent scope's value) or "milestone" (state carries forward sequentially irrespective of lexical depth). - -
-
- - Increments the counter block. Executing a step inside an existing step boundary pushes the state to the next depth index.
-
-
Attributes:
- counter: Required state identifier. -
-
- - Clones the active count variables into the lookup dictionary. Snapshot processing operates independently of the layout pass, allowing global retrieval regardless of physical placement.
-
-
Attributes:
- counter: Required state identifier.
- snapshot: Required dictionary assignment key. -
-
- - Retrieves the stored snapshot and projects it as innerHTML.
-
-
Attributes:
- snapshot: Required dictionary assignment key.
- key: Sub-state query property. Resolves dot-notation parameters (e.g., "count.status", "count.list"). Querying "count" triggers the style formatter and separator join parameters. (Default: "count"). Reading an empty counter (before a first step) causes an error to the console. -
-
-
-
- - - Cross reference -

- A two-pass mechanism bridging logical content with its physically paginated layout geometry. -

- - - - Registers the scoped content and its finalized layout page index into the reference dictionary.
-
-
Attributes:
- key: Required mapping identifier. -
-
- - Queries the reference dictionary and injects the output.
-
-
Attributes:
- key: Target mapping identifier.
- field: Data extraction parameter. Valid targets are "content" (the original HTML payload) or "page" (the evaluated layout page number). (Default: "content") -
-
-
-
- - - Annotation - - - Marks the scoped content for extraction and placement in the endnotes block. - - - Marks the scoped content for layout relocation to the active page's footer. - - - - - - Pagination - - - Physical document boundary generated dynamically by the paginator. Not for manual insertion. - - - Explicit directive terminating the current <RT·page> evaluation and pushing subsequent data to the next boundary. - - - -
- - - Memo Environment -

- The <RT·memo> container inherits all layout functionality from the Article configuration but enforces a static, print-ready CSS environment. -

-

- Implementation Note: This layout model is currently maintained as a legacy execution branch. Continued parity with the core layout engine is not guaranteed. -

-
- - - Global widgets - - - Injects a floating overlay permitting real-time CSS theme execution. Writes the selection to the browser's localStorage. - - - - - - Execution Pipeline Schedule - -

- In the RT-Style framework when a page is rendered, the JS code associated with the semantic elements modifies the DOM. It does so in multiple passes so that page numbering, counters, and cross references function without need for forward referencing or concern about dependencies between them. Consequently what is seen in the element view of the browser debug window will not have a one to one correspondence with the elements found in the document. -

- - - - Evaluates raw 1D streams. Injects section wrappers, maps counter states, evaluates math tokens, and isolates explicit string payloads. - - - Initial document chunking. Slices the continuous DOM into discrete <RT·page> boundaries based on height configurations. Modifies the tree aggressively. - - - Applies geometric CSS configurations to the generated pages. - - - Walks the tree to increment the state machines and populates snapshot variables. Must run after pagination to ensure page numbers exist. - - - Resolves mapping dictionaries bridging logical content with its physically paginated layout geometry. - - - Absorbs dimensional deltas. Expands page limits to accommodate space consumed by injected cross-references and generated indices. - - - -
- - - - - diff --git a/developer/authored/Manuscript.copy/Element/grid.js b/developer/authored/Manuscript.copy/Element/grid.js index 7b6c719..67ef078 100644 --- a/developer/authored/Manuscript.copy/Element/grid.js +++ b/developer/authored/Manuscript.copy/Element/grid.js @@ -21,6 +21,37 @@ } } + /* Compose the label, then place it. + + Aligning text inside a cell conflates two separate decisions. How the lines + of a label sit relative to one another is a property of the label; where + that block of lines sits within its cell is a property of the placement. + Ragged text pushed hard against the right edge of a cell is the result of + answering both with one text-align, and it reads as though the words were + spilled into the corner rather than set. + + The label is therefore given its own box, sized to its content and capped at + the width of the cell. The lines are set flush left within that box, which + is how prose is read, and the box is then placed in the cell — against the + data for a row heading, centred for a datum. It is the drawing convention: + compose the label, then attach it. + + text-wrap: balance evens the line lengths so that no line is left holding a + single stranded word. Where the browser does not implement it the text still + wraps, so nothing is lost beyond the evenness. + */ + function compose_label(el ,place){ + const inner = document.createElement('div'); + inner.className = 'RT_grid_label'; + inner.style.display = 'inline-block'; + inner.style.textAlign = 'left'; + inner.style.textWrap = 'balance'; + inner.style.maxWidth = '100%'; + while(el.firstChild) inner.appendChild(el.firstChild); + el.appendChild(inner); + el.style.textAlign = place; + } + const apply_style = function(el, cell, is_transposed, options, config) { el.style.padding = '0.25rem 0.5rem'; el.style.margin = '0'; @@ -54,9 +85,17 @@ } } - if (type === 'data' || type === 'x-label') { - el.style.textAlign = 'center'; + /* Row headings sit against the data they label; column headings and data + centre over their column. In every case the label's own lines are flush + left inside its box. */ + if (type === 'y-label') { + compose_label(el ,'right'); + } else if (type === 'corner') { + compose_label(el ,'right'); + } else if (type === 'x-label' || type === 'data') { + compose_label(el ,'center'); } + if ((is_transposed ? type === 'x-label' : type === 'y-label') || type === 'corner') { el.style.textAlign = 'right'; @@ -167,7 +206,24 @@ function render_model_html_dictionary(container_node, grid_state, options, config) { const wrapper = document.createElement('div'); wrapper.style.display = 'grid'; - wrapper.style.gridTemplateColumns = 'max-content auto'; + /* Recalcitrant wrapping in the key column. + + max-content makes the key column as wide as its longest key and refuses + to wrap at any width. One long key then takes most of the table and the + definitions are squeezed into a ribbon, which inverts the balance the + reader expects: the column carrying least information takes most room. + + fit-content(limit) resolves to min(max-content ,max(min-content ,limit)). + Keys keep their preferred width while they are short ,which is the common + case and the one worth optimizing for ,and a key too long for the ceiling + wraps rather than widening the column past it. Reluctant to wrap, not + unwilling — the whole of the intent is in that distinction. + + The ceiling is a proportion rather than a length so it holds across page + widths and themes. */ + const key_ceiling = (window.RT.config && window.RT.config.grid + && window.RT.config.grid.key_max_width) || '38%'; + wrapper.style.gridTemplateColumns = `fit-content(${key_ceiling}) 1fr`; wrapper.style.width = 'fit-content'; wrapper.style.maxWidth = '100%'; wrapper.className = `RT_grid_container ${options.css_class || ''}`; diff --git a/developer/authored/Manuscript.copy/Layout/article_tech_ref.js b/developer/authored/Manuscript.copy/Layout/article_tech_ref.js index 83aa270..e3e6e0d 100644 --- a/developer/authored/Manuscript.copy/Layout/article_tech_ref.js +++ b/developer/authored/Manuscript.copy/Layout/article_tech_ref.js @@ -83,7 +83,9 @@ let p_style = page_seq[i].style; p_style.position = "relative"; p_style.display = "block"; - p_style.padding = "3rem"; + // Single source of truth: the paginator measures against this same + // value, so a page's content box and the measuring container agree. + p_style.padding = ((RT.config && RT.config.page && RT.config.page.padding) || "3rem"); p_style.margin = "1.25rem auto"; p_style.backgroundColor = conf.surface_0; p_style.boxShadow = "0 0 0.625rem " + conf.brand_primary; diff --git a/developer/authored/Manuscript.copy/Layout/paginate.js b/developer/authored/Manuscript.copy/Layout/paginate.js index a0b08ef..34d9866 100644 --- a/developer/authored/Manuscript.copy/Layout/paginate.js +++ b/developer/authored/Manuscript.copy/Layout/paginate.js @@ -16,6 +16,15 @@ const page_conf = (RT.config && RT.config.page) ? RT.config.page : {}; const page_height_limit = page_conf.height_limit || 1000; + /* Pages carry padding, so the width available to content inside a page is + narrower than the article. Measuring at the article's width lets text wrap + less than it will when rendered, which reports every height short and makes + the paginator believe content fits when it does not. Pages then run long, + with no indication that anything went wrong. + + One value, read by both the paginator and whatever applies the geometry. */ + const page_padding = page_conf.padding || '3rem'; + /* --------------------------------------------------------------- Pagination tracing. @@ -92,11 +101,38 @@ measure_container = temp; return temp; } + /* Measure at the width content will actually occupy: a page's content box, + not the article. The probe carries the page padding so the arithmetic is + done by the browser in whatever units the padding was written in. */ + /* The probe stays in flow. An absolutely positioned box resolves a + percentage width against its nearest positioned ancestor ,which the + article is not ,so it would measure against the viewport instead — wider + than the article ,not narrower ,and every height would come back shorter + still. In flow ,100% is the article's content width ,which is what is + wanted. It is removed before anything else runs. */ + const probe = document.createElement('div'); + probe.style.visibility = 'hidden'; + probe.style.width = '100%'; + probe.style.height = '0'; + probe.style.overflow = 'hidden'; + probe.style.boxSizing = 'border-box'; + probe.style.padding = page_padding; + article.appendChild(probe); + const probe_style = window.getComputedStyle(probe); + const content_width = probe.clientWidth + - parseFloat(probe_style.paddingLeft || 0) + - parseFloat(probe_style.paddingRight || 0); + article.removeChild(probe); + const container = document.createElement('div'); const article_style = window.getComputedStyle(article); container.style.visibility = 'hidden'; container.style.position = 'absolute'; - container.style.width = article_style.width; + container.style.width = (content_width > 0 ? content_width + 'px' : article_style.width); + + RT.Debug.log('paginate' ,'measuring at page content width ' + + (content_width > 0 ? content_width + 'px' : article_style.width) + + ' (article ' + article_style.width + ' ,page padding ' + page_padding + ')'); container.style.fontFamily = article_style.fontFamily; container.style.fontSize = article_style.fontSize; container.style.lineHeight = article_style.lineHeight;