-# RT-style-JS_public
+# RT-style
A JavaScript based layout, themes, and semantic elements for HTML documents. Used for documents on RT projects.
<h1>Introduction</h1>
<p>
- This is a demonstration of the <RT-term>RT-style-JS_public</RT-term> engine in action.
+ This is a demonstration of the <RT-term>RT-style</RT-term> engine in action.
</p>
</RT-article>
</body>
If the project utilizes the **Harmony** skeleton, these paths map directly to standard locations:
* `<path_0>` is `shared/`.
-* `<path_1>` is `shared/third_party/RT-style-JS_public`.
+* `<path_1>` is `shared/third_party/RT-style`.
* The `setup.js` script is placed in the role-specific document folder (e.g., `administrator/document/setup.js`).
* `<relative_path_to_root>` is `../../` (stepping up out of `document/` and `administrator/` back to the root).
<head>
<meta charset="UTF-8">
<title>Release howto</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
+++ /dev/null
-window.RT_REPO_ROOT = "../../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/dictionary_style-directory.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/body_visibility_hidden.js"></script>');
--- /dev/null
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+html_update_to_3-1
+
+Replaces specific StyleRT script blocks with RT 3.1 loader blocks using
+literal string replacement.
+"""
+
+import sys ,os
+
+OLD_BLOCK = \
+""" <script src="setup.js"></script>
+ <script>
+ window.StyleRT.include('RT/theme');
+ window.StyleRT.include('RT/layout/article_tech_ref');
+ </script>"""
+
+NEW_BLOCK = \
+""" <script>
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
+ </script>"""
+
+def process_file(file_path: str) -> None:
+ try:
+ with open(file_path ,"r" ,encoding="utf-8") as f:
+ content = f.read()
+ except Exception as e:
+ print(f"Error reading {file_path}: {e}" ,file=sys.stderr)
+ return
+
+ # Normalize line endings to ensure a match across environments
+ normalized_content = content.replace("\r\n" ,"\n")
+ normalized_old = OLD_BLOCK.replace("\r\n" ,"\n")
+
+ if( normalized_old in normalized_content ):
+ new_content = normalized_content.replace(normalized_old ,NEW_BLOCK)
+ with open(file_path ,"w" ,encoding="utf-8") as f:
+ f.write(new_content)
+ print(f"Updated: {file_path}")
+ else:
+ print(f"Target lines not found in: {file_path}")
+
+def CLI() -> None:
+ args = sys.argv[1:]
+
+ if( not args or "-h" in args or "--help" in args ):
+ print("Usage: html_update_to_3-1 <file.html...>")
+ sys.exit(0)
+
+ for TM_f in args:
+ process_file(TM_f)
+
+if __name__ == "__main__":
+ CLI()
--- /dev/null
+#ifndef ExampleGreet·Greeter·ONCE
+#define ExampleGreet·Greeter·ONCE
+
+#include "Math.lib.c"
+
+void ExampleGreet·Greeter·hello_loop(int count);
+
+#ifdef ExampleGreet·Greeter
+ #include <stdio.h>
+
+ void ExampleGreet·Greeter·hello_loop(int count){
+ for(int TM = 0; TM < count; ++TM){
+ int current_count = ExampleGreet·Math·add(TM ,1);
+ printf("Hello iteration: %d\n" ,current_count);
+ }
+ }
+
+#endif // ExampleGreet·Greeter
+
+#endif // ExampleGreet·Greeter·ONCE
--- /dev/null
+#ifndef ExampleGreet·Math·ONCE
+#define ExampleGreet·Math·ONCE
+
+int ExampleGreet·Math·add(int a ,int b);
+
+#ifdef ExampleGreet·Math
+ int ExampleGreet·Math·add(int a ,int b){
+ return a + b;
+ }
+#endif // ExampleGreet·Math
+
+#endif // ExampleGreet·Math·ONCE
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "Math.lib.c"
+#include "Greeter.lib.c"
+
+void CLI(void){
+ int base_count = ExampleGreet·Math·add(1 ,2);
+ printf("Calculated base loop count: %d\n" ,base_count);
+ ExampleGreet·Greeter·hello_loop(base_count);
+}
+
+int main(int argc ,char **argv){
+ (void)argc;
+ (void)argv;
+
+ CLI();
+
+ return EXIT_SUCCESS;
+}
--- /dev/null
+// block_visibility_during_layout.js
+
+// 1. Hide the document immediately upon execution in the <head>
+document.documentElement.style.visibility = "hidden";
+
+// 2. Define the restoration function
+const restore_visibility = function() {
+ document.documentElement.style.visibility = "";
+ document.removeEventListener("RT_layout_complete", restore_visibility);
+ window.removeEventListener("load", restore_visibility);
+};
+
+// 3. Listen for a specific completion signal from the layout engine
+document.addEventListener("RT_layout_complete", restore_visibility);
+
+// 4. Structural Safety Net: If the layout engine fails or is never loaded,
+// restore visibility on the final window 'load' event so the page doesn't remain blank.
+window.addEventListener("load", restore_visibility);
+
--- /dev/null
+window.RT = window.RT || {};
+
+window.RT.load = function(module_path){
+ let target_module = module_path;
+
+ if(target_module === 'theme'){
+ let saved_theme = localStorage.getItem('RT_theme_preference');
+ if(!saved_theme){
+ saved_theme = 'dark_gold';
+ localStorage.setItem('RT_theme_preference', saved_theme);
+ }
+ target_module = 'theme/' + saved_theme;
+ }
+
+ let resolved_path = window.RT.dirpr_library + '/' + target_module;
+
+ if(!resolved_path.endsWith('.js')){
+ resolved_path = resolved_path + '.js';
+ }
+
+ document.write('<script src="' + resolved_path + '"></script>');
+};
--- /dev/null
+/*
+ General utilities for the RT Style library.
+*/
+
+window.RT = window.RT || {};
+
+// --- DEBUG SYSTEM ---
+window.RT.debug = {
+
+ // all debug messages enabled
+/*
+ active_tokens: new Set([
+ 'style', 'layout', 'pagination'
+ ,'selector', 'config', 'error'
+ ,'term'
+ ,'scroll'
+ ]),
+
+ active_tokens: new Set([
+ 'term'
+ ]),
+*/
+
+ active_tokens: new Set([
+ ]),
+
+ log: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.log(`[RT:${token}]`, message);
+ }
+ },
+
+ warn: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.warn(`[RT:${token}]`, message);
+ }
+ },
+
+ error: function(token, message) {
+ console.error(`[RT:${token}] CRITICAL:`, message);
+ },
+
+ enable: function(token) { this.active_tokens.add(token); console.log(`Enabled: ${token}`); },
+ disable: function(token) { this.active_tokens.delete(token); console.log(`Disabled: ${token}`); }
+};
+
+// --- UTILITIES ---
+window.RT.utility = {
+ // --- FONT PHYSICS ---
+ measure_ink_ratio: function(target_font, ref_font = null) {
+ const debug = window.RT.debug;
+ debug.log('layout', `Measuring ink ratio for ${target_font}`);
+
+ const canvas = document.createElement('canvas');
+ const ctx = canvas.getContext('2d');
+
+ if (!ref_font) {
+ const bodyStyle = window.getComputedStyle(document.body);
+ ref_font = bodyStyle.fontFamily;
+ }
+
+ const get_metrics = (font) => {
+ ctx.font = '100px ' + font;
+ const metrics = ctx.measureText('M');
+ return {
+ ascent: metrics.actualBoundingBoxAscent,
+ descent: metrics.actualBoundingBoxDescent
+ };
+ };
+
+ const ref_m = get_metrics(ref_font);
+ const target_m = get_metrics(target_font);
+
+ const ratio = ref_m.ascent / target_m.ascent;
+
+ return {
+ ratio: ratio,
+ baseline_diff: ref_m.descent - target_m.descent
+ };
+ },
+
+ // --- COLOR PHYSICS ---
+ is_color_light: function(color_string) {
+ // 1. HSL Check
+ if (color_string.startsWith('hsl')) {
+ const numbers = color_string.match(/\d+/g);
+ if (numbers && numbers.length >= 3) {
+ const lightness = parseInt(numbers[2]);
+ return lightness > 50;
+ }
+ }
+
+ // 2. RGB Check
+ const rgb = color_string.match(/\d+/g);
+ if (!rgb) {
+ return true;
+ }
+
+ const r = parseInt(rgb[0]);
+ const g = parseInt(rgb[1]);
+ const b = parseInt(rgb[2]);
+ const luma = (r * 299 + g * 587 + b * 114) / 1000;
+ return luma > 128;
+ },
+
+ is_block_content: function(element) {
+ return element.textContent.trim().includes('\n');
+ }
+};
--- /dev/null
+/home/Thomas/subu_data/developer/project/RT-style-JS_public/shared/linked-project/RT-style-JS_public/consumer/release/RT
\ No newline at end of file
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>RT Style System: Reference Manual</title>
+
+ <script>
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
+ </script>
+
+ </head>
+
+ <body>
+
+ <RT-article>
+ <RT-title author="Gemini" date="2026-06-17 15:19:00Z" title="RT Style System: Reference Manual"></RT-title>
+
+ <RT-TOC level="1"></RT-TOC>
+
+ <RT-chapter>Table of custom tags</RT-chapter>
+
+ <h2>Style tag reference</h2>
+
+ <table>
+ <thead>
+ <tr>
+ <th>Tag</th>
+ <th>Description</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td><RT-code><RT-article></RT-code></td>
+ <td>Article container.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-title></RT-code></td>
+ <td>
+ Title block and metadata.
+ </td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-TOC></RT-code></td>
+ <td>Compiles TOC.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-index></RT-code></td>
+ <td>Compiles an alphabetical glossary of defined terms.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-term></RT-code></td>
+ <td>Conventional term.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-term-em></RT-code></td>
+ <td>Forces emphasis for a term even after first occurrence.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-neologism></RT-code></td>
+ <td>Project specific term.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-neologism-em></RT-code></td>
+ <td>Forces emphasis for a neologism even after first occurrence.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-code></RT-code></td>
+ <td>Code span or code block.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-math></RT-code></td>
+ <td>Inline or block math.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-footnote></RT-code></td>
+ <td>Inline text to be extracted and rendered at the bottom of the page.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-cite></RT-code></td>
+ <td>Bibliographic citation marker.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-ref></RT-code></td>
+ <td>Dynamic cross reference to another element in the document.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-page></RT-code></td>
+ <td>Automatically inserted pagination container.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-chapter></RT-code></td>
+ <td>Chapter heading with implied page break.</td>
+ </tr>
+
+ <tr>
+ <td><RT-code><RT-page-break></RT-code></td>
+ <td>Explicit page break directive. Must be explicitly closed.</td>
+ </tr>
+ </tbody>
+ </table>
+
+ <RT-chapter>Architecture overview</RT-chapter>
+ <p>
+ The <RT-term>RT Style System</RT-term> is a client-side, JavaScript-driven publishing framework designed to turn raw HTML into high-readability technical documentation. Unlike standard CSS frameworks, RT uses JavaScript to handle complex layout tasks like <RT-neologism>ink-ratio balancing</RT-neologism> and dynamic pagination.
+ </p>
+
+ <h2>Pulling style files into a document</h2>
+ <p> Put `setup.js` in the directory, and include it at the top of the document,
+ `setup.js` points at the style files and is installation specific.</p>
+
+
+ <RT-chapter>Semantic tags</RT-chapter>
+ <p>
+ The system relies on a specific set of custom tags in the <RT-code>RT-</RT-code> namespace to separate meaning from presentation.
+ </p>
+
+ <h2>Terminology</h2>
+
+ <h3>Conventional terms</h3>
+ <p>
+ Use <RT-code><RT-term></RT-code> for standard, industry accepted technical terms. The system decorates only the first occurrence of each unique term so that the first appearance functions as a definition point, and later appearances do not overload the page with styling.
+ </p>
+
+ <RT-code>
+ The <RT-term>Singleton Pattern</RT-term> restricts instantiation...
+ </RT-code>
+
+ <p>
+ <em>Renders as:</em> The <RT-term>Singleton Pattern</RT-term> restricts instantiation...
+ </p>
+
+ <h3>Neologisms</h3>
+ <p>
+ Use <RT-code><RT-neologism></RT-code> for terms invented specifically for the current document or project. Neologisms are styled more strongly than conventional terms, visually distinguishing "jargon you should know" from "jargon we invented."
+ </p>
+
+ <RT-code>
+ We define the <RT-neologism>Hyper Tape</RT-neologism> as a construct...
+ </RT-code>
+
+ <p>
+ <em>Renders as:</em> We define the <RT-neologism>Hyper Tape</RT-neologism> as a construct...
+ </p>
+
+ <h3>First occurrence rule</h3>
+ <p>
+ The term system is intentionally conservative. For the tags <RT-code><RT-term></RT-code> and <RT-code><RT-neologism></RT-code>, only the first occurrence of a unique term is decorated. Subsequent mentions are rendered as normal prose.
+ </p>
+
+ <p>
+ Uniqueness is tracked by normalizing the term text (trimmed, then lowercased). This means that <RT-code>Symbol</RT-code> and <RT-code>symbol</RT-code> count as the same term.
+ </p>
+
+ <h3>Forced emphasis variants</h3>
+ <p>
+ Sometimes a later mention of a term should be emphasized again. For that purpose, the system provides explicit emphasis tags:
+ </p>
+
+ <ul>
+ <li><RT-code><RT-term-em></RT-code></li>
+ <li><RT-code><RT-neologism-em></RT-code></li>
+ </ul>
+
+ <p>
+ These variants are always decorated, even if the term appeared earlier.
+ </p>
+
+ <h3>Automatic definition anchors</h3>
+ <p>
+ For first occurrences, the term module automatically assigns an <RT-code>id</RT-code> attribute if one does not already exist. This establishes stable anchors for future indexing and linking.
+ </p>
+
+ <RT-code>
+ We define a <RT-term>Symbol</RT-term> as...
+ </RT-code>
+
+ <p>
+ The first occurrence will be given an id similar to <RT-code>def-symbol</RT-code>. For neologisms, an additional marker is used, for example <RT-code>def-neo-hyper-tape</RT-code>.
+ </p>
+
+ <h2>Technical content</h2>
+
+ <h3>Code</h3>
+ <p>
+ Use <RT-code><RT-code></RT-code>. If placed inline, it acts like a span. If placed as a block (with newlines), it acts like a pre formatted block with a theme aware border.
+ </p>
+
+ <RT-code>
+ # Block Code Example
+ def hello():
+ return "World"
+ </RT-code>
+
+ <h3>Mathematics</h3>
+ <p>
+ Use <RT-code><RT-math></RT-code>. The system auto detects if it is a block equation or inline variable and wraps it in MathJax delimiters.
+ </p>
+ <p>
+ Inline: Let <RT-math>x</RT-math> be the input.
+ </p>
+ <p>
+ Block:
+ </p>
+ <RT-math>
+ f(x) = \sum_{i=0}^{n} x_i
+ </RT-math>
+
+ <h2>References, citations, and indexing</h2>
+
+ <h3>Footnotes</h3>
+ <p>
+ Use <RT-code><RT-footnote></RT-code> to author footnote content directly inline with the text it references. During the layout phase, the pagination engine extracts the text, replaces the element with an invisible structural marker, and compiles a formatted footnote container at the bottom of the active <RT-code><RT-page></RT-code>.
+ </p>
+
+ <h3>Citations</h3>
+ <p>
+ Use <RT-code><RT-cite></RT-code> with the <RT-code>ref</RT-code> attribute to insert bibliographic citations. This ensures formatting remains consistent and separates citation data from standard prose.
+ </p>
+ <RT-code>
+ ... formalized as Basic Law V <RT-endnote ref="Gottlob Frege, Grundgesetze, 1903"></RT-endnote>.
+ </RT-code>
+
+ <h3>Dynamic cross referencing</h3>
+ <p>
+ A person authoring a technical document frequently refers back to tables, equations, or previous sections. Use <RT-code><RT-ref target="element-id"></RT-code> to link to specific content. This tag dynamically retrieves the numbering or caption of the target element, maintaining correct references even if preceding elements shift during editing.
+ </p>
+
+ <h3>Glossary indexing</h3>
+ <p>
+ Use <RT-code><RT-index></RT-code> at the end of the document to compile a glossary. Similar to the TOC compiler, this tag scans the DOM for automatic definition anchors produced by the term module and generates an alphabetized list of defined terms and neologisms.
+ </p>
+
+ <RT-chapter>Navigation and layout</RT-chapter>
+
+ <h2>Automatic table of contents</h2>
+ <p>
+ Use <RT-code><RT-TOC></RT-code> to insert a compiled table of contents. The tag scans the document <em>forward</em> from its current position to collect headings.
+ </p>
+
+ <h3>Explicit mode</h3>
+ <p>
+ Use the <RT-code>level="N"</RT-code> attribute to target a specific heading depth.
+ </p>
+ <ul>
+ <li><RT-code><RT-TOC level="1"></RT-code>: Collects all <RT-code><h1></RT-code> elements until the end of the document. Best for the main document index.</li>
+ <li><RT-code><RT-TOC level="2"></RT-code>: Collects all <RT-code><h2></RT-code> elements until it encounters the next <RT-code><h1></RT-code>. Best for chapter summaries.</li>
+ </ul>
+
+ <h3>Implicit mode</h3>
+ <p>
+ If no level is specified, the TOC scans backwards to find the nearest heading (for example H1) and assumes you want to collect children one level deeper (for example H2).
+ </p>
+ <p>
+ <em>Note: Implicit mode can fail if placed before the first heading of a section. Use explicit levels for robust results.</em>
+ </p>
+
+ <h2>The title block</h2>
+ <p>
+ Use <RT-code><RT-title></RT-code> as the first element in your <RT-code><body></RT-code> (before the article container). This tag produces a standardized, styled header block with the document title and metadata.
+ </p>
+
+ <h3>Attributes</h3>
+ <ul>
+ <li><RT-code>title</RT-code> (Required): The main heading of the document.</li>
+ <li><RT-code>author</RT-code> (Optional): The author's name. Renders in a bold accent color.</li>
+ <li><RT-code>date</RT-code> (Optional): The publication or revision date.</li>
+ </ul>
+
+ <h3>Example</h3>
+ <RT-code>
+ <RT-title
+ title="RT Style System: Reference Manual"
+ author="Gemini"
+ date="2026-06-17 15:19:00Z">
+ </RT-title>
+ </RT-code>
+
+ <p>
+ <em>Renders as:</em> A centered, high contrast H1 followed by a serif styled metadata row containing the author and date.
+ </p>
+
+ <h2>The article container</h2>
+ <p>
+ The root element must be <RT-code><RT-article></RT-code>. This is the boundary for the pagination logic.
+ </p>
+
+ <h2>Pagination</h2>
+ <p>
+ The script <RT-code>paginate_by_element.js</RT-code> scans the article. It calculates the height of every element (including margins) and bundles them into <RT-code><RT-page></RT-code> elements.
+ </p>
+ <p>
+ <RT-neologism>Soft Limit Pagination</RT-neologism>: The system attempts to keep headers with their following paragraphs. It will break a page early rather than stranding a header at the bottom.
+ </p>
+
+ <h2>Manual page breaks and chapters</h2>
+ <p>
+ A person can assert explicit control over the layout engine using the <RT-code><RT-page-break></RT-page-break></RT-code> tag. This tag forces the engine to close the current page and push all subsequent content to the top of the next page. Standard HTML5 parsing requires this custom tag to be explicitly closed; self-closing syntax will fail.
+ </p>
+ <p>
+ For major document divisions, use <RT-code><RT-chapter></RT-code>. The semantic processor translates this tag into an <RT-code><RT-page-break></RT-code> followed by an <RT-code><h1 class="RT-chapter"></RT-code>. This ensures the chapter starts on a new page, inherits standard typography, and is automatically indexed by the TOC engine.
+ </p>
+
+ <RT-chapter>Debugging</RT-chapter>
+
+ <h2>Debug tokens</h2>
+ <p>
+ RT provides a lightweight debug logging system in <RT-code>utility.js</RT-code>. Logging is controlled by a set of active debug tokens. Each log message is assigned a token, and the message prints only if that token is enabled.
+ </p>
+
+ <p>
+ Examples of common tokens include <RT-code>style</RT-code>, <RT-code>layout</RT-code>, <RT-code>pagination</RT-code>, <RT-code>selector</RT-code>, <RT-code>config</RT-code>, and <RT-code>term</RT-code>.
+ </p>
+
+ <h2>How logging is gated</h2>
+ <p>
+ Normal log and warning output are gated. The methods <RT-code>debug.log(token,message)</RT-code> and <RT-code>debug.warn(token,message)</RT-code> will print only when the token exists in <RT-code>debug.active_tokens</RT-code>.
+ </p>
+
+ <p>
+ This prevents the console from being flooded during normal use, while still allowing deep visibility during development.
+ </p>
+
+ <h2>Errors are always printed</h2>
+ <p>
+ Errors are treated differently. The method <RT-code>debug.error(token,message)</RT-code> always prints, regardless of token state. These messages represent failures that require attention.
+ </p>
+
+ <h2>Enabling and disabling tokens</h2>
+ <p>
+ Tokens are enabled or disabled in two ways: by editing the <RT-code>active_tokens</RT-code> set in <RT-code>utility.js</RT-code>, or at runtime by calling:
+ </p>
+
+ <RT-code>
+ window.RT.debug.enable('term')
+ window.RT.debug.disable('term')
+ </RT-code>
+
+ <p>
+ For example, the term system (<RT-code>RT_term.js</RT-code>) uses the <RT-code>term</RT-code> token. When that token is enabled, the module will print messages describing how terms were detected and which term definitions were assigned IDs.
+ </p>
+
+ <RT-chapter>Themes</RT-chapter>
+ <p>
+ The system supports hot swapping themes by changing the script import in the head.
+ </p>
+ <ul>
+ <li><strong>Dark:</strong> <RT-code>style/theme_dark_gold.js</RT-code> (Default)</li>
+ <li><strong>Light:</strong> <RT-code>style/theme_light_gold.js</RT-code></li>
+ </ul>
+
+ <RT-chapter>Manifest</RT-chapter>
+ <RT-code>
+ 2. utility.js (Math/Color physics)
+ 3. article_tech_ref.js (Typography and CSS injection)
+ 4. RT_code.js (Code block logic)
+ 5. RT_math.js (MathJax wrapper)
+ 6. RT_term.js (Term styling)
+ 7. RT_TOC.js (Navigation generator)
+ 8. paginate_by_element.js (Page splitter)
+ 9. page_fixed_glow.js (Visual page container)
+ </RT-code>
+
+ <RT-chapter>RT conventions</RT-chapter>
+ <p> Headings are first letter capitalized. Remaining words are as they would be in English prose.</p>
+
+ <RT-chapter>Exercises</RT-chapter>
+
+ <ol>
+ <li>
+ <p>
+ <strong>Term occurrences:</strong> If an author tags a word with <RT-code><RT-term></RT-code> five times in a document, how many times will the term be visually decorated by the styling engine? How does a person force the engine to decorate a later occurrence?
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>Term normalization:</strong> A person writes <RT-code><RT-term>Parse Tree</RT-term></RT-code> in paragraph one, and <RT-code><RT-term>parse tree</RT-term></RT-code> in paragraph two. Will the system assign a definition anchor to the second occurrence?
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>TOC compilation:</strong> What happens if a person places an <RT-code><RT-TOC></RT-code> tag without a level attribute before the first heading of a section?
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>Code and math formatting:</strong> How does the RT system determine whether to render an <RT-code><RT-code></RT-code> or <RT-code><RT-math></RT-code> element inline with the text versus as a standalone block?
+ </p>
+ </li>
+ <li>
+ <p>
+ <strong>Pagination logic:</strong> When the script bundles elements into an <RT-code><RT-page></RT-code> container, how does the soft limit pagination handle a heading that falls at the very bottom of the page capacity?
+ </p>
+ </li>
+ </ol>
+
+ </RT-article>
+ </body>
+</html>
--- /dev/null
+/*
+ Processes <RT-TOC> tags.
+ Populates each with headings found below it.
+
+ Attributes:
+ level="N" : Explicitly sets the target heading level (1-6).
+ e.g., level="1" collects H1s. level="2" collects H2s.
+ Stops collecting if it hits a heading of (level - 1) or higher.
+
+ Default (No attribute):
+ Context Aware. Looks backwards for the nearest heading H(N).
+ Targets H(N+1). Stops at the next H(N).
+
+First heading 1 1
+ First heading 2 2
+ Next heading 2 2
+Next heading 2 3
+
+*/
+
+window.RT = window.RT || {};
+
+window.RT.TOC = function(){
+ const debug = window.RT.debug || { log: function(){} };
+ const TOC_seq = document.querySelectorAll('rt-toc');
+
+ TOC_seq.forEach( (container ,TOC_index) => {
+ container.style.display = 'block';
+
+ // 1. Parse attribute: single number N or range A-B
+ const attr_val = container.getAttribute('level');
+ let start_level, end_level;
+
+ if (attr_val) {
+ const rangeMatch = attr_val.match(/^(\d)-(\d)$/);
+ if (rangeMatch) {
+ const a = parseInt(rangeMatch[1]);
+ const b = parseInt(rangeMatch[2]);
+ if (a >= 1 && a <= 6 && b >= 1 && b <= 6 && a <= b) {
+ start_level = a;
+ end_level = b;
+ if (debug.log) debug.log('TOC', `TOC #${TOC_index} range: H${a}-H${b}`);
+ } else {
+ if (debug.log) debug.log('TOC', `Invalid range "${attr_val}" → implicit mode`);
+ }
+ } else {
+ const single = parseInt(attr_val);
+ if (!isNaN(single) && single >= 1 && single <= 6) {
+ start_level = single;
+ end_level = single;
+ if (debug.log) debug.log('TOC', `TOC #${TOC_index} single level: H${single}`);
+ } else {
+ if (debug.log) debug.log('TOC', `Invalid level "${attr_val}" → implicit mode`);
+ }
+ }
+ }
+
+ // 2. Implicit mode (no attribute or invalid)
+ if (start_level === undefined || end_level === undefined) {
+ let context_level = 0;
+ let prev = container.previousElementSibling;
+ while (prev) {
+ const match = prev.tagName.match(/^H([1-6])$/);
+ if (match) {
+ context_level = parseInt(match[1]);
+ break;
+ }
+ prev = prev.previousElementSibling;
+ }
+ const target_level = Math.min(context_level + 1, 6);
+ start_level = target_level;
+ end_level = target_level;
+ if (debug.log) debug.log('TOC', `TOC #${TOC_index} implicit target: H${target_level}`);
+ }
+
+ // 3. Collect all matching headings until a higher-level heading stops us
+ const headings = [];
+ let next_el = container.nextElementSibling;
+ while (next_el) {
+ const match = next_el.tagName.match(/^H([1-6])$/);
+ if (match) {
+ const found_level = parseInt(match[1]);
+
+ // Stop if we hit a heading that is a parent of the lowest level we collect
+ if (found_level < start_level) break;
+
+ // Collect if within the requested range
+ if (found_level >= start_level && found_level <= end_level) {
+ // Ensure it has an id
+ if (!next_el.id) {
+ next_el.id = `TOC-ref-${TOC_index}-${found_level}-${headings.length}`;
+ }
+ headings.push({ el: next_el, level: found_level });
+ }
+ }
+ next_el = next_el.nextElementSibling;
+ }
+
+ // 4. Build the container (title + list)
+ container.innerHTML = '';
+ const title = document.createElement('h1');
+ title.textContent = start_level === 1 ? 'Table of Contents' : 'Section Contents';
+ title.style.textAlign = 'center';
+ container.appendChild(title);
+
+ if (headings.length === 0) return; // nothing to show
+
+ // Top-level list
+ const topList = document.createElement('ul');
+ topList.style.listStyle = 'none';
+ topList.style.paddingLeft = '0';
+ container.appendChild(topList);
+
+ // Stack of <ul> elements; index 0 = top-level list
+ const listStack = [topList];
+
+ for (const item of headings) {
+ // Depth relative to start_level
+ const depth = item.level - start_level; // 0 = top-level, 1 = sub-level, etc.
+
+ // Ensure we have the correct nesting depth
+ while (listStack.length - 1 > depth) {
+ // Pop until we are at the right depth
+ listStack.pop();
+ }
+
+ // If we need to go deeper, open new sub-lists inside the last <li>
+ while (listStack.length - 1 < depth) {
+ const parentList = listStack[listStack.length - 1];
+ const lastLi = parentList.lastElementChild;
+ if (lastLi) {
+ const subList = document.createElement('ul');
+ subList.style.listStyle = 'none';
+ subList.style.paddingLeft = '1.5rem'; // indentation for nested items
+ lastLi.appendChild(subList);
+ listStack.push(subList);
+ } else {
+ // No parent <li> yet – stay at current depth (flatten)
+ break;
+ }
+ }
+
+ // Create the <li> for this heading
+ const li = document.createElement('li');
+ li.style.marginBottom = '0.5rem';
+
+ const a = document.createElement('a');
+ a.href = `#${item.el.id}`;
+ a.textContent = item.el.textContent;
+ a.style.textDecoration = 'none';
+ a.style.color = 'inherit';
+ a.style.display = 'block';
+
+ a.onmouseover = () => a.style.color = 'var(--rt-brand-primary)';
+ a.onmouseout = () => a.style.color = 'inherit';
+
+ li.appendChild(a);
+ // Add to the current deepest list
+ listStack[listStack.length - 1].appendChild(li);
+ }
+ });
+};
--- /dev/null
+/*
+ Processes <RT-chapter> tags.
+ Transforms the tag into an <RT-page-break> followed by an <h1> with the RT-chapter class.
+*/
+window.RT = window.RT || {};
+
+window.RT.chapter = function(){
+ const debug = window.RT.debug || { log: function(){} };
+
+ document.querySelectorAll('RT-chapter').forEach( (el ,index) => {
+ if(debug.log) debug.log('chapter' ,`Processing chapter ${index + 1}`);
+
+ const brk = document.createElement('RT-page-break');
+ const h1 = document.createElement('h1');
+
+ h1.innerHTML = el.innerHTML;
+
+ if(el.className){
+ h1.className = el.className;
+ }
+ h1.classList.add('RT-chapter');
+
+ Array.from(el.attributes).forEach( (attr) => {
+ if(attr.name !== 'class'){
+ h1.setAttribute(attr.name ,attr.value);
+ }
+ });
+
+ el.parentNode.insertBefore(brk ,el);
+ el.replaceWith(h1);
+ });
+};
--- /dev/null
+/*
+ Processes <RT-CODE> tags.
+ Uses the central config or CSS variables from the theme.
+
+ Removes common indent from lines of code.
+*/
+function code() {
+ const RT = window.RT;
+ const U = RT.utility;
+ const debug = RT.debug;
+
+ debug.log('code', 'Starting render cycle.');
+
+ const metrics = U.measure_ink_ratio('monospace');
+
+ document.querySelectorAll('rt-code').forEach((el) => {
+ el.style.fontFamily = 'monospace';
+
+ const computed = window.getComputedStyle(el);
+ const accent = computed.getPropertyValue('--rt-accent').trim() || 'gold';
+
+ const is_block = U.is_block_content(el);
+ const parentColor = computed.color;
+ const is_text_light = U.is_color_light(parentColor);
+
+ const alpha = is_block ? 0.08 : 0.15;
+ const overlay = is_text_light ? `rgba(255,255,255,${alpha})` : `rgba(0,0,0,${alpha})`;
+ const text_color = is_text_light ? '#ffffff' : '#000000';
+
+ el.style.backgroundColor = overlay;
+
+ if (is_block) {
+ el.style.display = 'block';
+
+ // --- Tag-Relative Auto-Dedent Logic ---
+
+ // 1. Get Tag Indentation (The Anchor)
+ let tagIndent = '';
+ const prevNode = el.previousSibling;
+ if (prevNode && prevNode.nodeType === 3) {
+ const prevText = prevNode.nodeValue;
+ const lastNewLineIndex = prevText.lastIndexOf('\n');
+ if (lastNewLineIndex !== -1) {
+ tagIndent = prevText.substring(lastNewLineIndex + 1);
+ } else if (/^\s*$/.test(prevText)) {
+ tagIndent = prevText;
+ }
+ }
+
+ // 2. Calculate Common Leading Whitespace from Content
+ const rawLines = el.textContent.split('\n');
+
+ // Filter out empty lines for calculation purposes so they don't break the logic
+ const contentLines = rawLines.filter(line => line.trim().length > 0);
+
+ let commonIndent = null;
+
+ if (contentLines.length > 0) {
+ // Assume the first line sets the standard
+ const firstMatch = contentLines[0].match(/^\s*/);
+ commonIndent = firstMatch ? firstMatch[0] : '';
+
+ // Reduce the commonIndent if subsequent lines have LESS indentation
+ for (let i = 1; i < contentLines.length; i++) {
+ const line = contentLines[i];
+ // Determine how much of commonIndent this line shares
+ let j = 0;
+ while (j < commonIndent.length && j < line.length && commonIndent[j] === line[j]) {
+ j++;
+ }
+ commonIndent = commonIndent.substring(0, j);
+ if (commonIndent.length === 0) break; // Optimization
+ }
+ } else {
+ commonIndent = '';
+ }
+
+ // 3. Process Content
+ // Rule: Only strip if the Common Indent contains the Tag Indent (Safety Check)
+ // This handles the Emacs case: Tag is " ", Common is " ". " " starts with " ".
+ // We strip " ", leaving the code flush left.
+ let finalString = '';
+
+ if (commonIndent.length > 0 && commonIndent.startsWith(tagIndent)) {
+ const cleanedLines = rawLines.map(line => {
+ // Strip the common indent from valid lines
+ return line.startsWith(commonIndent) ? line.replace(commonIndent, '') : line;
+ });
+
+ // Remove artifact lines (first/last empty lines)
+ if (cleanedLines.length > 0 && cleanedLines[0].length === 0) {
+ cleanedLines.shift();
+ }
+ if (cleanedLines.length > 0 && cleanedLines[cleanedLines.length - 1].trim().length === 0) {
+ cleanedLines.pop();
+ }
+ finalString = cleanedLines.join('\n');
+ } else {
+ // Fallback: Code is to the left of the tag or weirdly formatted.
+ // Just trim the wrapper newlines.
+ finalString = el.textContent.trim();
+ }
+
+ el.textContent = finalString;
+ // --- End Indentation Logic ---
+
+ el.style.whiteSpace = 'pre';
+ el.style.fontSize = (parseFloat(computed.fontSize) * metrics.ratio * 0.95) + 'px';
+ el.style.padding = '1.2rem';
+ el.style.margin = '1.5rem 0';
+ el.style.borderLeft = `4px solid ${accent}`;
+ el.style.color = 'inherit';
+ } else {
+ el.style.display = 'inline';
+ const exactPx = parseFloat(computed.fontSize) * metrics.ratio * 1.0;
+ el.style.fontSize = exactPx + 'px';
+ el.style.padding = '0.1rem 0.35rem';
+ el.style.borderRadius = '3px';
+ const offsetPx = metrics.baseline_diff * (exactPx / 100);
+ el.style.verticalAlign = offsetPx + 'px';
+ el.style.color = text_color;
+ }
+ });
+
+ debug.log('code', 'Render cycle complete.');
+}
+
+window.RT = window.RT || {};
+window.RT.code = code;
--- /dev/null
+// developer/authored/RT/element/constraint.js
+window.RT = window.RT || {};
+
+window.RT.constraint = function(){
+ document.querySelectorAll('rt-constraint').forEach( (el) => {
+ el.style.display = 'block';
+ el.style.borderLeft = '4px solid var(--rt-state-warning)';
+ el.style.backgroundColor = 'var(--rt-surface-1)';
+ el.style.padding = '1rem';
+ el.style.margin = '1.5rem 0';
+ el.style.color = 'var(--rt-content-main)';
+ });
+};
--- /dev/null
+// developer/authored/RT/element/crossref.js
+window.RT = window.RT || {};
+
+window.RT.crossref = function(){
+ document.querySelectorAll('rt-crossref').forEach( (el) => {
+ el.style.color = 'var(--rt-brand-link)';
+ el.style.textDecoration = 'underline';
+ el.style.cursor = 'pointer';
+ el.style.fontWeight = '500';
+
+ // Note: To make this fully context-aware across soft limits,
+ // this module will eventually need to hook into the page
+ // registry built by paginate_by_element.js.
+ });
+};
--- /dev/null
+window.RT = window.RT || {};
+
+window.RT.end_note = function(){
+ const citations = document.querySelectorAll('rt-cite');
+ if(citations.length === 0) return;
+
+ const article = document.querySelector('rt-article');
+ if(!article) return;
+
+ // 1. Ensure the H1 is a direct child of the article so the TOC can see it
+ let endnotesHeader = document.getElementById('endnotes-header');
+ if (!endnotesHeader) {
+ endnotesHeader = document.createElement('h1');
+ endnotesHeader.id = 'endnotes-header';
+ endnotesHeader.innerText = 'Endnotes';
+ article.appendChild(endnotesHeader);
+ }
+
+ // 2. Locate or generate the endnotes list container
+ let endnoteContainer = document.querySelector('rt-endnotes');
+ if(!endnoteContainer) {
+ endnoteContainer = document.createElement('rt-endnotes');
+ article.appendChild(endnoteContainer);
+ }
+
+ // 3. Ensure the list structure exists
+ if(!endnoteContainer.querySelector('ol')) {
+ endnoteContainer.innerHTML = '<ol></ol>';
+ }
+
+ const list = endnoteContainer.querySelector('ol');
+
+ // Process each inline citation
+ citations.forEach((cite, index) => {
+ const refNum = index + 1;
+ const refText = cite.getAttribute('ref') || cite.innerHTML;
+
+ cite.innerHTML = `<a href="#note-${refNum}" id="cite-${refNum}">[${refNum}]</a>`;
+ cite.style.cursor = 'pointer';
+ cite.style.color = 'var(--rt-brand-link)';
+ cite.style.textDecoration = 'none';
+
+ // Append the corresponding entry into the endnotes list
+ const li = document.createElement('li');
+ li.id = `note-${refNum}`;
+ li.innerHTML = `${refText} <a href="#cite-${refNum}" style="text-decoration:none;">↩</a>`;
+ list.appendChild(li);
+ });
+
+ // Style the container
+ endnoteContainer.style.display = 'block';
+ endnoteContainer.style.marginTop = '1rem';
+ endnoteContainer.style.borderTop = '1px solid var(--rt-surface-3)';
+ endnoteContainer.style.paddingTop = '1rem';
+};
--- /dev/null
+/*
+ Processes <RT-MATH> tags.
+ JavaScript: math()
+ HTML Tag: <RT-MATH> (parsed as rt-math)
+*/
+function math(){
+ // querySelector treats 'rt-math' as case-insensitive for the tag
+ document.querySelectorAll('rt-math').forEach(el => {
+ if (el.textContent.startsWith('$')) return;
+
+ const is_block = el.parentElement.tagName === 'DIV' ||
+ el.textContent.includes('\n') ||
+ el.parentElement.childNodes.length === 1;
+
+ const delimiter = is_block ? '$$' : '$';
+ el.style.display = is_block ? 'block' : 'inline';
+ el.textContent = `${delimiter}${el.textContent.trim()}${delimiter}`;
+ });
+
+ // MathJax must find its config at window.MathJax
+ window.MathJax = {
+ tex: {
+ inlineMath: [['$', '$']],
+ displayMath: [['$$', '$$']]
+ }
+ };
+
+ const script = document.createElement('script');
+ script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
+ script.async = true;
+ document.head.appendChild(script);
+}
+
+window.RT = window.RT || {};
+window.RT.math = math;
--- /dev/null
+// developer/authored/RT/element/symbol.js
+window.RT = window.RT || {};
+
+window.RT.symbol = function(){
+ document.querySelectorAll('rt-symbol').forEach( (el) => {
+ el.style.fontFamily = '"Courier New", Courier, monospace';
+ el.style.fontWeight = '600';
+ el.style.padding = '0 0.1em';
+ });
+};
--- /dev/null
+/*
+ Processes <RT-TERM> and <RT-NEOLOGISM> tags.
+ - Styles only the first occurrence of a unique term/neologism.
+ - The "-em" variants (e.g., <RT-term-em>) are always styled.
+ - Automatically generates IDs for first occurrences for future indexing.
+*/
+
+window.RT = window.RT || {};
+
+window.RT.term = function() {
+ const RT = window.RT;
+
+ const debug = RT.debug || {
+ log: function() {}
+ ,warn: function() {}
+ ,error: function() {}
+ };
+
+ const DEBUG_TOKEN_S = 'term';
+
+ try {
+ // Track seen terms so only the first occurrence is decorated
+ const seen_terms_dpa = new Set();
+
+ const apply_style = (el, is_neologism_b) => {
+ el.style.fontStyle = 'italic';
+ el.style.fontWeight = is_neologism_b ? '600' : '500';
+ el.style.color = is_neologism_b
+ ? 'var(--rt-brand-secondary)'
+ : 'var(--rt-brand-primary)';
+ el.style.paddingRight = '0.1em'; // Compensation for italic slant
+ el.style.display = 'inline';
+ };
+
+ const clear_style = (el) => {
+ el.style.fontStyle = 'normal';
+ el.style.color = 'inherit';
+ el.style.fontWeight = 'inherit';
+ el.style.paddingRight = '';
+ el.style.display = '';
+ };
+
+ const selector_s = [
+ 'rt-term'
+ ,'rt-term-em'
+ ,'rt-neologism'
+ ,'rt-neologism-em'
+ ].join(',');
+
+ const tags_dpa = document.querySelectorAll(selector_s);
+
+ debug.log(DEBUG_TOKEN_S, `Scanning ${tags_dpa.length} term tags`);
+
+ tags_dpa.forEach(el => {
+ const tag_name_s = el.tagName.toLowerCase();
+ const is_neologism_b = tag_name_s.includes('neologism');
+ const is_explicit_em_b = tag_name_s.endsWith('-em');
+
+ const term_text_raw_s = (el.textContent || '').trim();
+ if (!term_text_raw_s.length) {
+ debug.warn(DEBUG_TOKEN_S, `Empty term tag encountered: <${tag_name_s}>`);
+ return;
+ }
+
+ // Normalize text for uniqueness tracking
+ const term_norm_s = term_text_raw_s.toLowerCase();
+
+ // Slug for ID generation (simple + stable)
+ const slug_s = term_norm_s.replace(/\s+/g, '-');
+
+ const is_first_occurrence_b = !seen_terms_dpa.has(term_norm_s);
+
+ if (is_explicit_em_b || is_first_occurrence_b) {
+ apply_style(el, is_neologism_b);
+
+ if (!is_explicit_em_b && is_first_occurrence_b) {
+ seen_terms_dpa.add(term_norm_s);
+
+ if (!el.id) {
+ el.id = `def-${is_neologism_b ? 'neo-' : ''}${slug_s}`;
+ debug.log(
+ DEBUG_TOKEN_S
+ ,`First occurrence: "${term_norm_s}" -> id="${el.id}"`
+ );
+ } else {
+ debug.log(
+ DEBUG_TOKEN_S
+ ,`First occurrence: "${term_norm_s}" (existing id="${el.id}")`
+ );
+ }
+ } else if (is_explicit_em_b) {
+ debug.log(
+ DEBUG_TOKEN_S
+ ,`Emphasized occurrence: "${term_norm_s}" (<${tag_name_s}>)`
+ );
+ }
+ } else {
+ // Subsequent mentions render as normal prose
+ clear_style(el);
+ }
+ });
+
+ debug.log(DEBUG_TOKEN_S, `Unique terms defined: ${seen_terms_dpa.size}`);
+ } catch (e) {
+ debug.error('error', `term failed: ${e && e.message ? e.message : String(e)}`);
+ }
+};
--- /dev/null
+class ThemeSelector extends HTMLElement{
+ connectedCallback(){
+ let current_theme = localStorage.getItem('RT_theme_preference');
+ if(!current_theme){
+ current_theme = 'dark_gold';
+ }
+
+ this.innerHTML = `
+ <div style="position:fixed; top:10px; right:10px; z-index:1000; background:#222; padding:10px; border:1px solid #555; color: white; font-family: sans-serif;">
+ <b>Theme Selection</b><br>
+ <label>
+ <input type="radio" name="rt-theme" value="dark_gold" ${current_theme === 'dark_gold' ? 'checked' : ''}> Dark Gold
+ </label><br>
+ <label>
+ <input type="radio" name="rt-theme" value="light_gold" ${current_theme === 'light_gold' ? 'checked' : ''}> Light Gold
+ </label>
+ </div>
+ `;
+
+ this.addEventListener( 'change' ,(e) => {
+ localStorage.setItem('RT_theme_preference' ,e.target.value);
+ location.reload();
+ } );
+ }
+}
+
+customElements.define('rt-theme-selector' ,ThemeSelector);
--- /dev/null
+/*
+ Processes <RT-TITLE> tags.
+ Generates a standard document header block.
+
+ Usage:
+ <RT-title title="..." author="..." date="..." copyright="..."></RT-title>
+*/
+window.RT = window.RT || {};
+
+window.RT.title = function() {
+ const debug = window.RT.debug || { log: function(){} };
+
+ document.querySelectorAll('rt-title').forEach(el => {
+ const title = el.getAttribute('title') || 'Untitled Document';
+ const author = el.getAttribute('author');
+ const date = el.getAttribute('date');
+ const copyright = el.getAttribute('copyright');
+
+ if (debug.log) debug.log('title', `Generating title block: ${title}`);
+
+ // Container
+ const container = document.createElement('div');
+ container.style.textAlign = 'center';
+ container.style.marginBottom = '3rem';
+ container.style.marginTop = '2rem';
+ container.style.borderBottom = '1px solid var(--rt-border-default)';
+ container.style.paddingBottom = '1.5rem';
+
+ // Main Title (H1)
+ const h1 = document.createElement('h1');
+ h1.textContent = title;
+ h1.style.margin = '0 0 0.8rem 0';
+ h1.style.border = 'none'; // Override standard H1 border
+ h1.style.padding = '0';
+ h1.style.color = 'var(--rt-brand-primary)';
+ h1.style.fontSize = '2.5em';
+ h1.style.lineHeight = '1.1';
+ h1.style.letterSpacing = '-0.03em';
+
+ container.appendChild(h1);
+
+ // Metadata Row (Author | Date)
+ if (author || date) {
+ const meta = document.createElement('div');
+ meta.style.color = 'var(--rt-content-muted)';
+ meta.style.fontStyle = 'italic';
+ meta.style.fontSize = '1.1em';
+ meta.style.fontFamily = '"Georgia", "Times New Roman", serif'; // Classy serif
+
+ const parts = [];
+ if (author) parts.push(`<span style="font-weight:600; color:var(--rt-brand-secondary)">${author}</span>`);
+ if (date) parts.push(date);
+
+ meta.innerHTML = parts.join(' — ');
+ container.appendChild(meta);
+ }
+
+ // Copyright Row
+ if (copyright) {
+ const copy_div = document.createElement('div');
+ copy_div.style.color = 'var(--rt-content-muted)';
+ copy_div.style.fontSize = '0.9em';
+ copy_div.style.marginTop = '0.5rem';
+ // Automatically injects the copyright symbol
+ copy_div.innerHTML = `© ${copyright}`;
+ container.appendChild(copy_div);
+ }
+
+ // Replace the raw tag with the generated block
+ el.replaceWith(container);
+ });
+};
--- /dev/null
+// debug messages don't work here, because core/utility isn't loaded until after the function runs.
+(function(){
+ const RT = window.RT = window.RT || {};
+ const debug = RT.debug || { log: function(){} };
+
+ debug.log('scroll', "1. Initializing script.");
+
+ // 1. Intercept native history restoration immediately
+ if ('scrollRestoration' in history) {
+ history.scrollRestoration = 'manual';
+ debug.log('scroll', "2. history.scrollRestoration set to manual.");
+ }
+
+ // 2. Read coordinate from memory before any layout shifts occur
+ const raw_target = sessionStorage.getItem('RT_saved_y');
+ const target_y = raw_target !== null ? parseInt(raw_target, 10) : 0;
+
+ // 3. Determine if the execution is a page reload
+ let is_reload = false;
+ if (window.performance) {
+ const nav_entries = performance.getEntriesByType("navigation");
+ if (nav_entries.length > 0) {
+ is_reload = (nav_entries[0].type === "reload");
+ } else if (performance.navigation) {
+ is_reload = (performance.navigation.type === 1);
+ }
+ }
+
+ debug.log('scroll', `3. Target Y: ${target_y} | Is Reload: ${is_reload}`);
+
+ // 4. The Lock
+ let is_layout_locked = true;
+
+ // Helper to ensure we only signal completion once
+ function unlock_layout() {
+ if (!is_layout_locked) return;
+ is_layout_locked = false;
+ debug.log('scroll', "10. Layout fully unlocked. Emitting completion signal.");
+ document.dispatchEvent(new Event("RT_layout_complete"));
+ }
+
+ // 5. Declare Dependencies
+ RT.load('element/chapter');
+ RT.load('element/endnote');
+ RT.load('element/math');
+ RT.load('element/code');
+ RT.load('element/term');
+ RT.load('element/TOC');
+ RT.load('element/title');
+ RT.load('element/theme_selector');
+ RT.load('element/symbol');
+ RT.load('element/constraint');
+ RT.load('element/crossref');
+
+ RT.load('layout/paginate_by_element');
+ RT.load('layout/page_fixed_glow');
+
+ // 6. The Typography Layout
+ RT.article = function(){
+ RT.config = RT.config || {};
+ RT.config.article = {
+ font_family: '"Noto Sans", "Segoe UI", "Helvetica Neue", sans-serif'
+ ,line_height: "1.8"
+ ,font_size: "16px"
+ ,font_weight: "400"
+ ,max_width: "820px"
+ ,margin: "0 auto"
+ };
+
+ if( RT.config.theme && RT.config.theme.meta_is_dark === false ){
+ RT.config.article.font_weight = "600";
+ }
+
+ const conf = RT.config.article;
+ const article_seq = document.querySelectorAll("RT-article");
+
+ if(article_seq.length === 0) return;
+
+ for(let i = 0; i < article_seq.length; i++){
+ let style = article_seq[i].style;
+ style.display = "block";
+ style.fontFamily = conf.font_family;
+ style.fontSize = conf.font_size;
+ style.lineHeight = conf.line_height;
+ style.fontWeight = conf.font_weight;
+ style.maxWidth = conf.max_width;
+ style.margin = conf.margin;
+ style.padding = "0 20px";
+ style.color = "var(--rt-content-main)";
+ }
+
+ window.RT = window.RT || {};
+ window.RT.config = window.RT.config || {};
+ window.RT.config.page = window.RT.config.page || {};
+ window.RT.config.page.height_limit = 900;
+
+ const style_node = document.createElement("style");
+ style_node.innerHTML = `
+ body, html, rt-article {
+ overflow-anchor: none !important;
+ }
+
+ rt-article {
+ font-family: 'Noto Sans JP', Arial, sans-serif;
+ background-color: var(--rt-surface-0);
+ color: var(--rt-content-main);
+ max-width: 46.875rem !important;
+ box-sizing: border-box !important;
+ }
+
+ rt-article:not(:has(rt-page)) {
+ padding: 3rem !important;
+ }
+
+ rt-article:has(rt-page) {
+ padding: 0 !important;
+ }
+
+ rt-article rt-page {
+ position: relative;
+ display: block;
+ padding: 3rem;
+ margin: 1.25rem auto;
+ background-color: var(--rt-surface-0);
+ box-shadow: 0 0 0.625rem var(--rt-brand-primary);
+ }
+
+ rt-article h1 {
+ font-size: 1.5rem;
+ text-align: center;
+ color: var(--rt-brand-primary);
+ font-weight: 500;
+ margin-top: 1.5rem;
+ line-height: 1.15;
+ }
+ rt-article h2 {
+ font-size: 1.25rem;
+ color: var(--rt-brand-secondary);
+ text-align: left;
+ margin-top: 2rem;
+ margin-left: 0;
+ }
+ rt-article h3 {
+ font-size: 1.125rem;
+ color: var(--rt-brand-tertiary);
+ text-align: left;
+ margin-top: 1.5rem;
+ margin-left: 4ch;
+ }
+ rt-article h4 {
+ font-size: 1.05rem;
+ color: var(--rt-content-main);
+ font-weight: 600;
+ text-align: left;
+ margin-top: 1.25rem;
+ margin-left: 8ch;
+ }
+
+ rt-article p,
+ rt-article ul,
+ rt-article ol {
+ color: var(--rt-content-main);
+ text-align: justify;
+ margin-bottom: 1rem;
+ margin-left: 0;
+ }
+ rt-article li {
+ margin-bottom: 0.5rem;
+ }
+
+ rt-article rt-code {
+ font-family: 'Courier New', Courier, monospace;
+ background-color: var(--rt-surface-code);
+ padding: 0.125rem 0.25rem;
+ color: var(--rt-content-main);
+ }
+
+ rt-article img {
+ max-width: 100%;
+ height: auto;
+ display: block;
+ margin: 1.5rem auto;
+ }
+ `;
+ document.head.appendChild(style_node);
+ };
+
+ // 7. The Execution Sequence
+ function run_semantics(){
+ debug.log('scroll' ,`4. run_semantics starting.`);
+ if(RT.theme) RT.theme();
+ if(RT.endnote) RT.endnote();
+ RT.article();
+ if(RT.title) RT.title();
+ if(RT.term) RT.term();
+ if(RT.math) RT.math();
+ if(RT.code) RT.code();
+ if(RT.symbol) RT.symbol();
+ if(RT.constraint) RT.constraint();
+ if(RT.crossref) RT.crossref();
+
+ if( window.MathJax && MathJax.Hub && MathJax.Hub.Queue ){
+ MathJax.Hub.Queue( ["Typeset" ,MathJax.Hub] ,run_layout );
+ }else{
+ run_layout();
+ }
+ }
+
+ function run_layout() {
+ debug.log('scroll', `5. run_layout starting.`);
+
+ if(RT.chapter) RT.chapter();
+ if(RT.TOC) RT.TOC();
+ if(RT.paginate_by_element) RT.paginate_by_element();
+ if(RT.page) RT.page();
+
+ debug.log('scroll', `6. Pagination complete.`);
+
+ let final_target = target_y;
+ let use_hash = false;
+
+ if (window.location.hash && !is_reload) {
+ const hash_target = document.getElementById(window.location.hash.substring(1));
+ if (hash_target) {
+ use_hash = true;
+ }
+ }
+
+ debug.log('scroll', `7. Commencing viewport enforce loop. Mode: ${use_hash ? 'HASH' : 'Y-COORDINATE'}`);
+ enforce_scroll(final_target, use_hash, 0);
+ }
+
+ // 8. The Enforcer Logic
+ function enforce_scroll(target, use_hash, attempts) {
+ if (attempts > 15) {
+ debug.log('scroll', "8. Scroll enforcement timed out. Unlocking.");
+ unlock_layout();
+ return;
+ }
+
+ if (use_hash) {
+ const hash_target = document.getElementById(window.location.hash.substring(1));
+ if (hash_target) {
+ hash_target.scrollIntoView();
+ debug.log('scroll', `8a. Attempt ${attempts}: Scrolled to Hash Target. Y is now ${window.scrollY}`);
+ }
+ } else {
+ window.scrollTo(0, target);
+ debug.log('scroll', `8b. Attempt ${attempts}: Scrolled to Y=${target}. Current Y is ${window.scrollY}`);
+ }
+
+ let is_successful = false;
+ if (use_hash) {
+ is_successful = true;
+ } else {
+ is_successful = (Math.abs(window.scrollY - target) < 5 || target === 0);
+ }
+
+ if (is_successful && document.body.scrollHeight > 1000) {
+ debug.log('scroll', `9. Viewport anchored successfully.`);
+
+ setTimeout(() => {
+ if (!use_hash && Math.abs(window.scrollY - target) >= 5) {
+ debug.log('scroll', `9a. Browser late-stage rebellion detected. Re-enforcing.`);
+ enforce_scroll(target, use_hash, attempts + 1);
+ } else {
+ unlock_layout();
+ }
+ }, 100);
+ } else {
+ setTimeout(() => enforce_scroll(target, use_hash, attempts + 1), 50);
+ }
+ }
+
+ // 9. The Ledger
+ let scroll_timer;
+ window.addEventListener('scroll', () => {
+ if (is_layout_locked) return;
+ clearTimeout(scroll_timer);
+ scroll_timer = setTimeout(() => {
+ sessionStorage.setItem('RT_saved_y', window.scrollY);
+ debug.log('scroll', `X. User stopped scrolling. Saved Y: ${window.scrollY}`);
+ }, 200);
+ }, { passive: true });
+
+ window.addEventListener('beforeunload', () => {
+ is_layout_locked = true;
+ debug.log('scroll', "Y. Page unloading. Scroll listener locked.");
+ });
+
+ // 10. Bind to DOM Ready
+ document.addEventListener('DOMContentLoaded', run_semantics);
+
+})();
+
--- /dev/null
+/*
+ <head>
+ <script>
+ window.RT.load('layout/memo_state_dept');
+ </script>
+ </head>
+ <body>
+ <RT-memo>
+ </RT-memo>
+ </body>
+*/
+
+(function(){
+ const RT = window.RT = window.RT || {};
+
+ // 1. Declare Dependencies
+ RT.load('core/utility');
+ RT.load('element/title');
+ RT.load('element/term');
+ RT.load('element/TOC');
+ RT.load('core/body_visibility_visible');
+
+ // 2. The Typography Layout
+ RT.memo_state_dept = function(){
+ const body = document.body;
+ const html = document.documentElement;
+
+ // Force strict print colors regardless of user system settings
+ html.style.backgroundColor = "white";
+ body.style.backgroundColor = "white";
+ body.style.color = "black";
+
+ // Target the new semantic tag
+ const memo_seq = document.querySelectorAll("RT-memo");
+ if(memo_seq.length === 0) return;
+
+ for(let i = 0; i < memo_seq.length; i++){
+ let style = memo_seq[i].style;
+ style.display = "block";
+ style.fontFamily = '"Times New Roman", Times, serif';
+ style.fontSize = "12pt";
+ style.lineHeight = "1.15";
+ // 8.5 inch standard width minus 1-inch margins on each side
+ style.maxWidth = "6.5in";
+ style.margin = "1in auto";
+ style.padding = "0";
+ style.textAlign = "left";
+ style.color = "black";
+ }
+ };
+
+ // 3. The Execution Sequence
+ const run_semantics = function(){
+ RT.memo_state_dept();
+
+ if(RT.title) RT.title();
+ if(RT.term) RT.term();
+ if(RT.TOC) RT.TOC();
+
+ run_layout();
+ };
+
+ const run_layout = function(){
+ if(RT.body_visibility_visible) RT.body_visibility_visible();
+ };
+
+ // 4. Bind to DOM Ready
+ document.addEventListener('DOMContentLoaded' ,run_semantics);
+
+})();
--- /dev/null
+/*
+ Page Layout: Fixed Glow
+ Standard: Theme 1.0
+ Description: A variable-height container with a glowing border effect that matches the active theme.
+*/
+(function(){
+ const RT = window.RT = window.RT || {};
+
+ RT.page = function() {
+ RT.config = RT.config || {};
+
+ // Default Configuration
+ const defaults = {
+ width: "100%"
+ ,min_height: "15rem" // Replaces fixed height
+ ,padding: "3rem"
+ ,margin: "4rem auto"
+
+ ,bg_color: "var(--rt-surface-0)"
+ ,border_color: "var(--rt-brand-primary)"
+ ,text_color: "var(--rt-brand-primary)"
+
+ ,shadow: "drop-shadow(0px 0px 15px var(--rt-brand-primary))"
+ };
+
+ RT.config.page = Object.assign({}, defaults, RT.config.page || {});
+
+ const conf = RT.config.page;
+ const style_id = 'rt-page-fixed-glow';
+
+ if (!document.getElementById(style_id)) {
+ const style_el = document.createElement('style');
+ style_el.id = style_id;
+
+ style_el.textContent = `
+ /* Reset page counter on the article container */
+ rt-article {
+ counter-reset: rt-page-counter;
+ }
+
+ rt-page {
+ display: block;
+ position: relative;
+ box-sizing: border-box;
+ overflow: hidden;
+
+ /* Dimensions */
+ width: ${conf.width};
+ min-height: ${conf.min_height};
+ margin: ${conf.margin};
+ padding: ${conf.padding};
+
+ /* Theming */
+ background-color: ${conf.bg_color};
+ border: 1px solid ${conf.border_color};
+
+ /* The "Glow" Effect */
+ filter: ${conf.shadow};
+
+ /* Counter Increment */
+ counter-increment: rt-page-counter;
+ }
+
+ /* Page Numbering */
+ rt-page::after {
+ content: "Page " counter(rt-page-counter);
+ position: absolute;
+ bottom: 1.5rem;
+ right: 3rem;
+
+ font-family: "Noto Sans", sans-serif;
+ font-size: 0.9rem;
+ font-weight: bold;
+
+ color: ${conf.text_color};
+ opacity: 0.8;
+ pointer-events: none;
+ }
+ `;
+ document.head.appendChild(style_el);
+ }
+ };
+})();
--- /dev/null
+window.RT.paginate_by_element = function () {
+ const RT = window.RT;
+ const debug = RT.debug || { log: function(){}, error: function(){} };
+ const page_conf = (RT.config && RT.config.page) ? RT.config.page : {};
+ const page_height_limit = page_conf.height_limit || 1000;
+
+ let measureContainer = null;
+
+ // =========================================================
+ // 1. DOM Measurement Utilities
+ // =========================================================
+ function getElHeight(el) {
+ const wasInDOM = el.parentNode !== null;
+ if (!wasInDOM) document.body.appendChild(el);
+ const rect = el.getBoundingClientRect();
+ const style = window.getComputedStyle(el);
+ const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom);
+ if (!wasInDOM) el.remove();
+ return (rect.height || 0) + (margin || 0);
+ }
+
+ function getMeasureContainer() {
+ if (measureContainer && measureContainer.parentNode) return measureContainer;
+ const article = document.querySelector('RT-article');
+ if (!article) {
+ const temp = document.createElement('div');
+ temp.style.visibility = 'hidden';
+ temp.style.position = 'absolute';
+ temp.style.width = '100%';
+ document.body.appendChild(temp);
+ measureContainer = temp;
+ return temp;
+ }
+ const container = document.createElement('div');
+ const articleStyle = window.getComputedStyle(article);
+ container.style.visibility = 'hidden';
+ container.style.position = 'absolute';
+ container.style.width = articleStyle.width;
+ container.style.fontFamily = articleStyle.fontFamily;
+ container.style.fontSize = articleStyle.fontSize;
+ container.style.lineHeight = articleStyle.lineHeight;
+ container.style.fontWeight = articleStyle.fontWeight;
+ document.body.appendChild(container);
+ measureContainer = container;
+ return container;
+ }
+
+ function measureFragment(frag) {
+ const container = getMeasureContainer();
+ container.appendChild(frag);
+ const h = getElHeight(frag);
+ container.removeChild(frag);
+ return h;
+ }
+
+ // =========================================================
+ // STEP 1: PREPARE FOOTNOTES (Strip and tag)
+ // =========================================================
+ const article_seq = document.querySelectorAll('RT-article');
+ if (article_seq.length === 0) {
+ debug.error('pagination', 'No <RT-article> elements found. Pagination aborted.');
+ return;
+ }
+
+ const footnote_registry = {};
+ let footnote_counter = 1;
+
+ Array.from(article_seq).forEach(article => {
+ // Bulletproof extraction: immune to XML/HTML case-sensitivity parsing quirks
+ const all_nodes = Array.from(article.querySelectorAll('*'));
+ const raw_footnotes = all_nodes.filter(node => node.tagName.toLowerCase() === 'rt-footnote');
+
+ raw_footnotes.forEach(fn => {
+ const id = footnote_counter++;
+ footnote_registry[id] = fn.innerHTML; // Save the payload
+
+ // Trim any standard HTML whitespace immediately preceding the tag
+ const prev = fn.previousSibling;
+ if (prev && prev.nodeType === Node.TEXT_NODE) {
+ prev.textContent = prev.textContent.replace(/\s+$/, '');
+ }
+
+ // Replace with a zero-height marker that rides along with the text
+ const marker = document.createElement('rt-fn-marker');
+ marker.setAttribute('data-id', id);
+
+ if (fn.parentNode) {
+ fn.parentNode.replaceChild(marker, fn);
+ }
+ });
+ });
+
+ // =========================================================
+ // Splitting Logic (Clean and undisturbed)
+ // =========================================================
+ function isSplittable(el) {
+ const tag = el.tagName;
+ if (tag === 'UL' || tag === 'OL') {
+ const items = Array.from(el.children).filter(c => c.tagName === 'LI');
+ if (items.length === 0) return null;
+
+ const itemHeights = items.map(li => getElHeight(li));
+ const emptyClone = el.cloneNode(false);
+ const overhead = getElHeight(emptyClone);
+
+ el._splitInfo = { type: 'list', itemHeights, overhead, offset: 0 };
+ return makeListSplitter(el, el._splitInfo);
+ }
+
+ if (tag === 'TABLE') {
+ const thead = el.querySelector('thead');
+ const tbody = el.querySelector('tbody');
+ const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows);
+ if (rows.length === 0) return null;
+
+ const theadHeight = thead ? getElHeight(thead) : 0;
+ const rowHeights = rows.map(row => getElHeight(row));
+
+ const emptyClone = el.cloneNode(false);
+ if (thead) emptyClone.appendChild(thead.cloneNode(true));
+ emptyClone.appendChild(document.createElement('tbody'));
+ const overhead = getElHeight(emptyClone) - theadHeight;
+
+ el._splitInfo = { type: 'table', rowHeights, overhead, theadHeight, offset: 0 };
+ return makeTableSplitter(el, el._splitInfo);
+ }
+ return null;
+ }
+
+ function makeListSplitter(el, info) {
+ return (remaining) => {
+ const children = Array.from(el.children).filter(c => c.tagName === 'LI');
+ const start = info.offset;
+
+ let bestCount = 0;
+ let bestHeight = 0;
+ const tempList = el.cloneNode(false);
+
+ for (let i = 0; i < children.length; i++) {
+ const itemClone = children[i].cloneNode(true);
+ tempList.appendChild(itemClone);
+ const fragHeight = measureFragment(tempList);
+ if (fragHeight <= remaining) {
+ bestCount = i + 1;
+ bestHeight = fragHeight;
+ } else {
+ tempList.removeChild(itemClone);
+ break;
+ }
+ }
+
+ if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 };
+
+ const first = el.cloneNode(false);
+ for (let i = 0; i < bestCount; i++) {
+ first.appendChild(children[i].cloneNode(true));
+ }
+
+ let rest = null;
+ if (bestCount < children.length) {
+ rest = el.cloneNode(false);
+ for (let i = bestCount; i < children.length; i++) {
+ rest.appendChild(children[i].cloneNode(true));
+ }
+
+ if (el.tagName === 'OL') {
+ const currentStart = parseInt(el.getAttribute('start'), 10) || 1;
+ rest.setAttribute('start', currentStart + bestCount);
+ }
+
+ rest._splitInfo = {
+ type: 'list',
+ itemHeights: info.itemHeights,
+ overhead: info.overhead,
+ offset: start + bestCount
+ };
+ }
+
+ return { first, rest, firstHeight: bestHeight };
+ };
+ }
+
+ function makeTableSplitter(el, info) {
+ const thead = el.querySelector('thead');
+ const createShell = () => {
+ const shell = el.cloneNode(false);
+ if (thead) shell.appendChild(thead.cloneNode(true));
+ const newTbody = document.createElement('tbody');
+ shell.appendChild(newTbody);
+ return shell;
+ };
+
+ return (remaining) => {
+ const tbody = el.querySelector('tbody');
+ const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows);
+ const start = info.offset;
+
+ let bestCount = 0;
+ let bestHeight = 0;
+ const tempTable = createShell();
+ const tempBody = tempTable.querySelector('tbody');
+
+ for (let i = 0; i < rows.length; i++) {
+ tempBody.appendChild(rows[i].cloneNode(true));
+ const h = measureFragment(tempTable);
+ if (h <= remaining) {
+ bestCount = i + 1;
+ bestHeight = h;
+ } else {
+ tempBody.removeChild(tempBody.lastChild);
+ break;
+ }
+ }
+
+ if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 };
+
+ const first = createShell();
+ const firstBody = first.querySelector('tbody');
+ for (let i = 0; i < bestCount; i++) {
+ firstBody.appendChild(rows[i].cloneNode(true));
+ }
+
+ let rest = null;
+ if (bestCount < rows.length) {
+ rest = createShell();
+ const restBody = rest.querySelector('tbody');
+ for (let i = bestCount; i < rows.length; i++) {
+ restBody.appendChild(rows[i].cloneNode(true));
+ }
+
+ rest._splitInfo = {
+ type: 'table',
+ rowHeights: info.rowHeights,
+ overhead: info.overhead,
+ theadHeight: info.theadHeight,
+ offset: start + bestCount
+ };
+ }
+
+ return { first, rest, firstHeight: bestHeight };
+ };
+ }
+
+ // =========================================================
+ // STEP 2: NORMAL PAGINATOR
+ // =========================================================
+ function paginateArticle(article) {
+ const raw_element_seq = Array.from(article.children).filter(el =>
+ !['SCRIPT', 'STYLE', 'RT-PAGE'].includes(el.tagName)
+ );
+
+ if (raw_element_seq.length === 0) return;
+
+ const page_seq = [];
+ let current_batch_seq = [];
+ let current_h = 0;
+ let i = 0;
+
+ while (i < raw_element_seq.length) {
+ const el = raw_element_seq[i];
+ const splitter = isSplittable(el);
+
+ if (splitter) {
+ const remaining = page_height_limit - current_h;
+ const { first, rest, firstHeight } = splitter(remaining);
+
+ if (first) {
+ current_batch_seq.push(first);
+ current_h += firstHeight;
+
+ if (rest) {
+ raw_element_seq.splice(i, 1, rest);
+ } else {
+ raw_element_seq.splice(i, 1);
+ }
+ } else {
+ if (current_batch_seq.length === 0) {
+ const frame = document.createElement('rt-scroll-frame');
+ frame.style.display = 'block';
+ frame.style.overflowY = 'auto';
+ frame.style.maxHeight = page_height_limit + 'px';
+ frame.appendChild(el);
+ current_batch_seq.push(frame);
+ i++;
+ } else {
+ page_seq.push(current_batch_seq);
+ current_batch_seq = [];
+ current_h = 0;
+ raw_element_seq[i] = rest || el;
+ }
+ }
+ continue;
+ }
+
+
+ // --- Ordinary (non-splittable) element ---
+ const h = getElHeight(el);
+ const is_RT_page_break = el.tagName && el.tagName.toLowerCase() === 'rt-page-break';
+
+ if( (is_RT_page_break || current_h + h > page_height_limit) && current_batch_seq.length > 0 ){
+ let backtrack_seq = [];
+ let backtrack_h = 0;
+
+ while (current_batch_seq.length > 0) {
+ const last = current_batch_seq[current_batch_seq.length - 1];
+ if (!/^H[1-6]/.test(last.tagName)) break;
+ const popped = current_batch_seq.pop();
+ backtrack_seq.unshift(popped);
+ backtrack_h += getElHeight(popped);
+ }
+
+ if (current_batch_seq.length > 0) {
+ page_seq.push(current_batch_seq);
+ current_batch_seq = backtrack_seq;
+ current_h = backtrack_h;
+ } else {
+ page_seq.push(backtrack_seq);
+ current_batch_seq = [];
+ current_h = 0;
+ }
+ }
+
+ current_batch_seq.push(el);
+ current_h += h;
+ i++;
+ }
+
+ if (current_batch_seq.length > 0) {
+ page_seq.push(current_batch_seq);
+ }
+
+ // Rebuild article with <rt-page> wrappers
+ article.innerHTML = '';
+ let p = 0;
+ while (p < page_seq.length) {
+ const batch = page_seq[p];
+ const page_el = document.createElement('rt-page');
+ page_el.id = `page-${p + 1}`;
+ batch.forEach(item => page_el.appendChild(item));
+ article.appendChild(page_el);
+ p++;
+ }
+ }
+
+ // Execute pagination
+ Array.from(article_seq).forEach(article => paginateArticle(article));
+
+ // =========================================================
+ // STEP 3: RESOLVE FOOTNOTES & EXPAND PAGES
+ // =========================================================
+ Array.from(article_seq).forEach(article => {
+ const rendered_pages = article.querySelectorAll('rt-page');
+
+ Array.from(rendered_pages).forEach(page => {
+ // Bulletproof extraction for the markers
+ const all_page_nodes = Array.from(page.querySelectorAll('*'));
+ const markers = all_page_nodes.filter(node => node.tagName.toLowerCase() === 'rt-fn-marker');
+
+ if (markers.length === 0) return;
+
+ // Construct the footer block for this page
+ const fn_container = document.createElement('div');
+ fn_container.className = 'rt-footnote-container';
+ fn_container.style.borderTop = '1px solid var(--rt-border-default)';
+ fn_container.style.marginTop = '2rem';
+ fn_container.style.paddingTop = '1rem';
+ fn_container.style.fontSize = '0.9em';
+
+ markers.forEach(marker => {
+ const id = marker.getAttribute('data-id');
+ const html = footnote_registry[id];
+
+ // Replace the invisible marker with the visible naked superscript link
+ const sup = document.createElement('sup');
+ sup.innerHTML = `<a href="#fn-${id}" id="fn-ref-${id}" style="color: var(--rt-brand-link); text-decoration: none;">${id}</a>`;
+
+ if (marker.parentNode) {
+ marker.parentNode.replaceChild(sup, marker);
+ }
+
+ // Append the actual text to the footer with a clean, print-ready number format
+ const fn_line = document.createElement('div');
+ fn_line.id = `fn-${id}`;
+ fn_line.style.marginBottom = '0.5rem';
+ fn_line.innerHTML = `<span style="padding-right: 0.5em; font-weight: 600;">${id}.</span>${html}`;
+ fn_container.appendChild(fn_line);
+ });
+
+ // Attach the footer. The page organically stretches to fit.
+ page.appendChild(fn_container);
+ });
+ });
+
+ // Cleanup
+ if (measureContainer && measureContainer.parentNode) {
+ measureContainer.remove();
+ measureContainer = null;
+ }
+};
--- /dev/null
+/*
+ Theme: Inverse Wheat (Dark)
+ Standard: Theme 1.0
+ Description: High contrast Amber on Deep Charcoal.
+*/
+( function(){
+ const RT = window.RT = window.RT || {};
+
+ RT.theme = function(){
+ RT.config = RT.config || {};
+
+ // THEME 1.0 DATA CONTRACT
+ RT.config.theme = {
+ meta_is_dark: true
+ ,meta_name: "Inverse Wheat"
+
+ // --- SURFACES (Depth & Container Hierarchy) ---
+ ,surface_0: "hsl(0, 0%, 5%)" // App Background (Deepest)
+ ,surface_1: "hsl(0, 0%, 10%)" // Sidebar / Nav / Panels
+ ,surface_2: "hsl(0, 0%, 14%)" // Cards / Floating Elements
+ ,surface_3: "hsl(0, 0%, 18%)" // Modals / Dropdowns / Popovers
+ ,surface_input: "hsl(0, 0%, 12%)" // Form Inputs
+ ,surface_code: "hsl(0, 0%, 11%)" // Code Block Background
+ ,surface_select: "hsl(45, 100%, 15%)" // Text Selection Highlight
+
+ // --- CONTENT (Text & Icons) ---
+ ,content_main: "hsl(50, 60%, 85%)" // Primary Reading Text
+ ,content_muted: "hsl(36, 15%, 60%)" // Metadata, subtitles
+ ,content_subtle: "hsl(36, 10%, 40%)" // Placeholders, disabled states
+ ,content_inverse: "hsl(0, 0%, 5%)" // Text on high-contrast buttons
+
+ // --- BRAND & ACTION (The "Wheat" Identity) ---
+ ,brand_primary: "hsl(45, 100%, 50%)" // Main Action / H1 / Focus Ring
+ ,brand_secondary: "hsl(38, 90%, 65%)" // Secondary Buttons / H2
+ ,brand_tertiary: "hsl(30, 60%, 70%)" // Accents / H3
+ ,brand_link: "hsl(48, 100%, 50%)" // Hyperlinks (High Visibility)
+
+ // --- BORDERS & DIVIDERS ---
+ ,border_faint: "hsl(36, 20%, 15%)" // Subtle separation
+ ,border_default: "hsl(36, 20%, 25%)" // Standard Card Borders
+ ,border_strong: "hsl(36, 20%, 40%)" // Active states / Inputs
+
+ // --- STATE & FEEDBACK (Earth Tones) ---
+ ,state_success: "hsl(100, 50%, 45%)" // Olive Green
+ ,state_warning: "hsl(35, 90%, 55%)" // Burnt Orange
+ ,state_error: "hsl(0, 60%, 55%)" // Brick Red
+ ,state_info: "hsl(200, 40%, 55%)" // Slate Blue
+
+ // --- SYNTAX HIGHLIGHTING (For Code) ---
+ ,syntax_keyword: "hsl(35, 100%, 65%)" // Orange
+ ,syntax_string: "hsl(75, 50%, 60%)" // Sage Green
+ ,syntax_func: "hsl(45, 90%, 70%)" // Light Gold
+ ,syntax_comment: "hsl(36, 15%, 45%)" // Brown/Gray
+ };
+
+ // --- APPLY THEME ---
+ const palette = RT.config.theme;
+ const body = document.body;
+ const html = document.documentElement;
+
+ // 1. Paint Base
+ html.style.backgroundColor = palette.surface_0;
+ body.style.backgroundColor = palette.surface_0;
+ body.style.color = palette.content_main;
+
+ // 2. Export Variables (Standardization)
+ const s = body.style;
+ for (const [key, value] of Object.entries(palette)) {
+ s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
+ }
+
+
+ // 3. Global Overrides
+ const style_id = 'rt-global-overrides';
+ if (!document.getElementById(style_id)) {
+ const style = document.createElement('style');
+ style.id = style_id;
+ style.textContent = `
+ ::selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
+ ::-moz-selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
+
+ ::-webkit-scrollbar { width: 12px; }
+ ::-webkit-scrollbar-track { background: var(--rt-surface-0); }
+ ::-webkit-scrollbar-thumb {
+ background: var(--rt-border-default);
+ border: 2px solid var(--rt-surface-0);
+ border-radius: 8px;
+ }
+ ::-webkit-scrollbar-thumb:hover { background: var(--rt-brand-secondary); }
+
+ /* --- Citation & Endnote Styling --- */
+ rt-cite a, .rt-inline-cite a, rt-endnotes a {
+ color: var(--rt-brand-link);
+ text-decoration: none;
+ }
+ rt-cite a:hover, .rt-inline-cite a:hover, rt-endnotes a:hover {
+ text-decoration: underline;
+ }
+ rt-cite, .rt-inline-cite {
+ font-size: 1em;
+ vertical-align: baseline;
+ padding: 0 0.15em;
+ }
+
+ /* --- Image Inversion for Diagrams --- */
+ img.rt-diagram {
+ filter: invert(1) hue-rotate(180deg);
+ }
+ `;
+
+ document.head.appendChild(style);
+ }
+ };
+
+} )();
--- /dev/null
+/*
+ Theme: Classic Wheat (Light)
+ Standard: Theme 1.0
+ Description: Warm paper tones with Burnt Orange accents.
+*/
+( function(){
+ const RT = window.RT = window.RT || {};
+
+ RT.theme_light = function(){
+ RT.config = RT.config || {};
+
+ // THEME 1.0 DATA CONTRACT
+ RT.config.theme = {
+ meta_is_dark: false
+ ,meta_name: "Classic Wheat"
+
+ // --- SURFACES ---
+ ,surface_0: "hsl(40, 30%, 94%)" // App Background (Cream/Linen)
+ ,surface_1: "hsl(40, 25%, 90%)" // Sidebar (Slightly darker beige)
+ ,surface_2: "hsl(40, 20%, 98%)" // Cards (Lighter, almost white)
+ ,surface_3: "hsl(0, 0%, 100%)" // Modals (Pure White)
+ ,surface_input: "hsl(40, 20%, 98%)" // Form Inputs
+ ,surface_code: "hsl(40, 15%, 90%)" // Code Block Background
+ ,surface_select: "hsl(45, 100%, 85%)" // Text Selection Highlight
+
+ // --- CONTENT ---
+ ,content_main: "hsl(30, 20%, 20%)" // Deep Umber (Not Black)
+ ,content_muted: "hsl(30, 15%, 45%)" // Medium Brown
+ ,content_subtle: "hsl(30, 10%, 65%)" // Light Brown/Gray
+ ,content_inverse: "hsl(40, 30%, 94%)" // Text on dark buttons
+
+ // --- BRAND & ACTION ---
+ ,brand_primary: "hsl(30, 90%, 35%)" // Burnt Orange (Action)
+ ,brand_secondary: "hsl(35, 70%, 45%)" // Rust / Gold
+ ,brand_tertiary: "hsl(25, 60%, 55%)" // Copper
+ ,brand_link: "hsl(30, 100%, 35%)" // Link Color
+
+ // --- BORDERS ---
+ ,border_faint: "hsl(35, 20%, 85%)"
+ ,border_default: "hsl(35, 20%, 75%)"
+ ,border_strong: "hsl(35, 20%, 55%)"
+
+ // --- STATE & FEEDBACK ---
+ ,state_success: "hsl(100, 40%, 40%)" // Forest Green
+ ,state_warning: "hsl(30, 90%, 50%)" // Persimmon
+ ,state_error: "hsl(0, 60%, 45%)" // Crimson
+ ,state_info: "hsl(200, 50%, 45%)" // Navy Blue
+
+ // --- SYNTAX ---
+ ,syntax_keyword: "hsl(20, 90%, 45%)" // Rust
+ ,syntax_string: "hsl(100, 35%, 35%)" // Ivy Green
+ ,syntax_func: "hsl(300, 30%, 40%)" // Muted Purple
+ ,syntax_comment: "hsl(35, 10%, 60%)" // Light Brown
+ };
+
+ // --- APPLY THEME ---
+ const palette = RT.config.theme;
+ const body = document.body;
+ const html = document.documentElement;
+
+ html.style.backgroundColor = palette.surface_0;
+ body.style.backgroundColor = palette.surface_0;
+ body.style.color = palette.content_main;
+
+ const s = body.style;
+ for (const [key, value] of Object.entries(palette)) {
+ s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
+ }
+ };
+} )();
--- /dev/null
+/*
+ Theme: Golden Wheat (Light) - "Spanish Gold Edition"
+ File: style/theme-light-gold.js
+ Standard: Theme 1.0
+ Description: Light Parchment background with Oxblood Red ink.
+*/
+( function(){
+ const RT = window.RT = window.RT || {};
+
+ RT.theme = function(){
+ RT.config = RT.config || {};
+
+ RT.config.theme = {
+ meta_is_dark: false
+ ,meta_name: "Golden Wheat (Yellow)"
+
+ // --- SURFACES (Light Parchment) ---
+ // Shifted lightness up to 94% for a "whiter" feel that still holds the yellow tint.
+ ,surface_0: "hsl(48, 50%, 94%)" // Main Page: Fine Parchment
+ ,surface_1: "hsl(48, 40%, 90%)" // Panels: Slightly darker
+ ,surface_2: "hsl(48, 30%, 97%)" // Cards: Very light
+ ,surface_3: "hsl(0, 0%, 100%)" // Popups
+ ,surface_input: "hsl(48, 20%, 96%)"
+ ,surface_code: "hsl(48, 25%, 88%)" // Distinct Code BG
+ ,surface_select: "hsl(10, 70%, 85%)" // Red Highlight
+
+ // --- CONTENT (Deep Ink) ---
+ ,content_main: "hsl(10, 25%, 7%)" // Deep Warm Black (Ink)
+ ,content_muted: "hsl(10, 15%, 35%)" // Dark Grey-Red
+ ,content_subtle: "hsl(10, 10%, 55%)"
+ ,content_inverse: "hsl(48, 50%, 90%)"
+
+ // --- BRAND & ACTION (The Red Spectrum) ---
+ ,brand_primary: "hsl(12, 85%, 30%)" // H1 (Deep Oxblood)
+ ,brand_secondary: "hsl(10, 80%, 35%)" // H2 (Garnet)
+ ,brand_tertiary: "hsl(8, 70%, 40%)" // H3 (Brick)
+ ,brand_link: "hsl(12, 90%, 35%)" // Link
+
+ // --- BORDERS ---
+ ,border_faint: "hsl(45, 30%, 80%)"
+ ,border_default: "hsl(45, 30%, 70%)" // Pencil Grey
+ ,border_strong: "hsl(12, 50%, 40%)"
+
+ // --- STATE ---
+ ,state_success: "hsl(120, 40%, 30%)"
+ ,state_warning: "hsl(25, 90%, 45%)"
+ ,state_error: "hsl(0, 75%, 35%)"
+ ,state_info: "hsl(210, 60%, 40%)"
+
+ // --- SYNTAX ---
+ ,syntax_keyword: "hsl(0, 75%, 35%)"
+ ,syntax_string: "hsl(100, 35%, 25%)"
+ ,syntax_func: "hsl(15, 85%, 35%)"
+ ,syntax_comment: "hsl(45, 20%, 50%)"
+ };
+
+ // --- APPLY THEME ---
+ const palette = RT.config.theme;
+ const body = document.body;
+ const html = document.documentElement;
+
+ html.style.backgroundColor = palette.surface_0;
+ body.style.backgroundColor = palette.surface_0;
+ body.style.color = palette.content_main;
+
+ const s = body.style;
+ for (const [key, value] of Object.entries(palette)) {
+ s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
+ }
+
+ // Global overrides
+ const style_id = 'rt-global-overrides';
+ if (!document.getElementById(style_id)) {
+ const style = document.createElement('style');
+ style.id = style_id;
+ style.textContent = `
+ ::selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
+ ::-moz-selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
+
+ ::-webkit-scrollbar { width: 12px; }
+ ::-webkit-scrollbar-track { background: var(--rt-surface-0); }
+ ::-webkit-scrollbar-thumb {
+ background: var(--rt-border-default);
+ border: 2px solid var(--rt-surface-0);
+ border-radius: 8px;
+ }
+ ::-webkit-scrollbar-thumb:hover { background: var(--rt-brand-secondary); }
+
+ rt-article p, rt-article li {
+ text-shadow: 0px 0px 0.5px rgba(0,0,0, 0.2);
+ }
+
+ .MathJax, .MathJax_Display, .mjx-chtml {
+ color: var(--rt-content-main) !important;
+ fill: var(--rt-content-main) !important;
+ stroke: var(--rt-content-main) !important;
+ }
+ `;
+ document.head.appendChild(style);
+ }
+ };
+
+} )();
--- /dev/null
+import os
+import glob
+import base64
+import re
+from weasyprint import HTML
+
+# Find the image file
+img_path = None
+for search_dir in ['/mnt/data', '/tmp', '.']:
+ for root, dirs, files in os.walk(search_dir):
+ if 'money_circle.jpeg' in files:
+ img_path = os.path.join(root, 'money_circle.jpeg')
+ break
+ if img_path:
+ break
+
+img_b64 = ""
+if img_path:
+ with open(img_path, "rb") as f:
+ img_b64 = base64.b64encode(f.read()).decode('utf-8')
+
+html_content = """
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>On Cybersecurity and Commonsense</title>
+ </head>
+ <body>
+ </body>
+</html>
+"""
+
+# String replacements (hyphens, 'may', 'just')
+html_content = html_content.replace('—', '-')
+html_content = re.sub(r'\bmay\b', 'can', html_content)
+html_content = re.sub(r'\bMay\b', 'Can', html_content)
+html_content = re.sub(r'\bjust\b', 'merely', html_content)
+html_content = re.sub(r'\bJust\b', 'Merely', html_content)
+
+# Title block replacement
+title_match = re.search(r'<rt-title\s+title="(.*?)"\s+author="(.*?)"\s+date="(.*?)"\s+copyright="(.*?)"\s*>\s*</rt-title>', html_content, re.IGNORECASE | re.DOTALL)
+if title_match:
+ title, author, date, copyright_text = title_match.groups()
+ if not copyright_text.startswith('©') and not copyright_text.startswith('©'):
+ copyright_text = f"© {copyright_text}"
+
+ title_html = f'''
+ <div class="title-block">
+ <h1>{title}</h1>
+ <p class="meta-author">{author}</p>
+ <p class="meta-date">{date}</p>
+ <p class="meta-copyright">{copyright_text}</p>
+ </div>
+ '''
+ html_content = html_content[:title_match.start()] + title_html + html_content[title_match.end():]
+
+# TOC replacement
+headings = re.findall(r'<h([12])\s*(id="(.*?)")?>(.*?)</h\1>', html_content, re.IGNORECASE)
+toc_html = '<div class="toc-block"><h2>Table of Contents</h2><ul>'
+for idx, (level, _, id_val, text) in enumerate(headings):
+ if not id_val:
+ id_val = f"heading-{idx}"
+ html_content = re.sub(f'<h{level}>{text}</h{level}>', f'<h{level} id="{id_val}">{text}</h{level}>', html_content, count=1)
+ toc_html += f'<li class="toc-h{level}"><a href="#{id_val}">{text}</a></li>'
+toc_html += '</ul></div>'
+
+html_content = re.sub(r'<RT-TOC[^>]*></RT-TOC>', toc_html, html_content, flags=re.IGNORECASE)
+
+# Image replacement
+if img_b64:
+ html_content = re.sub(r'src="money_circle\.jpeg"', f'src="data:image/jpeg;base64,{img_b64}" class="content-image"', html_content)
+
+# Custom tags
+html_content = re.sub(r'<RT-article>', '<div class="rt-article">', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'</RT-article>', '</div>', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'<RT-term>', '<span class="rt-term">', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'</RT-term>', '</span>', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'<RT-term-em>', '<span class="rt-term-em">', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'</RT-term-em>', '</span>', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'<RT-neologism>', '<span class="rt-neologism">', html_content, flags=re.IGNORECASE)
+html_content = re.sub(r'</RT-neologism>', '</span>', html_content, flags=re.IGNORECASE)
+
+def replace_rt_math(m):
+ content = m.group(1)
+ if '\n' in content:
+ return f'<div class="math-block math">{content}</div>'
+ return f'<span class="math-inline math">{content}</span>'
+
+def replace_rt_code(m):
+ content = m.group(1)
+ if '\n' in content:
+ return f'<pre class="rt-code-block">{content}</pre>'
+ return f'<code class="rt-code-inline">{content}</code>'
+
+html_content = re.sub(r'<RT-math>(.*?)</RT-math>', replace_rt_math, html_content, flags=re.IGNORECASE | re.DOTALL)
+html_content = re.sub(r'<RT-code>(.*?)</RT-code>', replace_rt_code, html_content, flags=re.IGNORECASE | re.DOTALL)
+
+css_styles = """
+ @page {
+ size: letter;
+ margin: 25mm 20mm;
+ background-color: #FAFAFA;
+ @bottom-center {
+ content: counter(page);
+ font-family: 'Georgia', serif;
+ font-size: 10pt;
+ color: #555;
+ }
+ }
+ body {
+ margin: 0;
+ padding: 0;
+ font-family: 'Georgia', serif;
+ font-size: 11pt;
+ line-height: 1.6;
+ color: #2c3e50;
+ background-color: #FAFAFA;
+ }
+ *, *::before, *::after { box-sizing: border-box; }
+
+ h1, h2, h3, h4 {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ color: #1A365D;
+ page-break-after: avoid;
+ }
+ h1 { font-size: 20pt; margin-top: 1.5em; margin-bottom: 0.5em; border-bottom: 2px solid #E2E8F0; padding-bottom: 0.2em; }
+ h2 { font-size: 16pt; margin-top: 1.5em; margin-bottom: 0.5em; }
+ p { margin-bottom: 1em; text-align: justify; }
+ ul, ol { margin-bottom: 1em; padding-left: 2em; }
+ li { margin-bottom: 0.5em; }
+
+ .title-block { text-align: center; margin-bottom: 4em; padding: 2em 0; border-bottom: 3px solid #1A365D; }
+ .title-block h1 { font-size: 26pt; border: none; margin-top: 0; margin-bottom: 0.5em; color: #0F2040; }
+ .title-block .meta-author { font-size: 14pt; font-weight: bold; margin: 0.2em 0; }
+ .title-block .meta-date { font-size: 12pt; color: #555; font-style: italic; margin: 0.2em 0; }
+ .title-block .meta-copyright { font-size: 10pt; color: #777; margin-top: 1em; }
+
+ .toc-block { background-color: #F0F4F8; padding: 20px; border-radius: 5px; margin-bottom: 3em; page-break-after: always; }
+ .toc-block h2 { margin-top: 0; border: none; }
+ .toc-block ul { list-style-type: none; padding-left: 0; }
+ .toc-h1 { font-weight: bold; margin-top: 0.8em; }
+ .toc-h2 { padding-left: 1.5em; font-size: 0.95em; }
+ .toc-block a { text-decoration: none; color: #2c3e50; }
+
+ .rt-term { font-weight: bold; color: #2980B9; }
+ .rt-term-em { font-weight: bold; font-style: italic; color: #2980B9; }
+ .rt-neologism { font-variant: small-caps; font-weight: bold; color: #C0392B; }
+ .rt-code-inline { font-family: 'Courier New', Courier, monospace; background-color: #EAECEE; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
+ .rt-code-block { font-family: 'Courier New', Courier, monospace; background-color: #F4F6F7; padding: 15px; border-left: 4px solid #7F8C8D; border-radius: 3px; font-size: 0.9em; overflow-x: auto; white-space: pre-wrap; }
+
+ .math { font-family: 'Times New Roman', serif; font-style: italic; font-weight: bold; color: #2C3E50; }
+ .math-block { text-align: center; margin: 1.5em 0; font-size: 1.2em; background-color: #F9EBEA; padding: 10px; border-radius: 5px; }
+ .math-inline { font-size: 1.05em; }
+
+ table { width: 100%; border-collapse: collapse; margin: 2em 0; }
+ th, td { padding: 10px; text-align: left; vertical-align: top; }
+ th { background-color: #1A365D; color: white; font-weight: bold; }
+ tr { border-bottom: 1px solid #ddd; }
+ hr { border: 0; border-top: 1px solid #eee; }
+
+ .content-image { display: block; max-width: 90%; margin: 2em auto; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
+"""
+
+final_html = f"<!DOCTYPE html><html><head><meta charset='UTF-8'><style>{css_styles}</style></head><body>{html_content}</body></html>"
+HTML(string=final_html).write_pdf("White_Paper_Cybersecurity_Legislation.pdf")
+print("Success")
--- /dev/null
+import re
+import os
+import glob
+from weasyprint import HTML
+
+def process_rt_html(html_str, out_file):
+ # Fix escaped dollar signs
+ html_str = html_str.replace('\\$', '$')
+
+ # Remove hardcoded page breaks that cause blank pages
+ html_str = re.sub(r'<div\s+style=["\']page-break-[^>]*["\']>\s*</div>', '', html_str, flags=re.IGNORECASE)
+
+ css = """
+ <style>
+ @page {
+ size: A4;
+ margin: 25mm 20mm;
+ background-color: #faf9f6;
+ }
+ *, *::before, *::after { box-sizing: border-box; }
+ body {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 11pt;
+ color: #2c3e50;
+ line-height: 1.6;
+ margin: 0;
+ padding: 0;
+ background-color: #faf9f6;
+ }
+ h1, h2, h3 {
+ color: #1a252f;
+ page-break-after: avoid;
+ font-family: 'Georgia', serif;
+ }
+ h1 {
+ font-size: 16pt;
+ border-bottom: 2px solid #d4af37;
+ padding-bottom: 5px;
+ margin-top: 2em;
+ }
+ h2 {
+ font-size: 14pt;
+ margin-top: 1.5em;
+ color: #2c3e50;
+ }
+ h3 {
+ font-size: 12pt;
+ margin-top: 1.2em;
+ color: #34495e;
+ }
+ .rt-title-block {
+ text-align: center;
+ margin: -25mm -20mm 30px -20mm;
+ padding: 35px 20mm;
+ background-color: #1a252f;
+ color: #ecf0f1;
+ }
+ .rt-title-block h1 {
+ color: #d4af37;
+ border: none;
+ font-size: 20pt;
+ margin: 0 0 15px 0;
+ padding: 0;
+ }
+ .rt-meta {
+ font-family: 'Georgia', serif;
+ font-size: 11pt;
+ color: #bdc3c7;
+ font-style: italic;
+ }
+ .rt-copyright {
+ font-size: 9pt;
+ color: #95a5a6;
+ margin-top: 10px;
+ }
+ .rt-term {
+ border-bottom: 1px dashed #7f8c8d;
+ font-style: italic;
+ color: #2980b9;
+ }
+ .rt-term-plain {
+ font-style: normal;
+ }
+ .rt-neologism {
+ font-weight: bold;
+ color: #c0392b;
+ }
+ .rt-neologism-plain {
+ font-weight: normal;
+ }
+ .toc {
+ background-color: #ffffff;
+ border-left: 4px solid #d4af37;
+ padding: 20px;
+ margin: 25px 0;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ /* Ensure the TOC breaks naturally, without forcing an empty page */
+ }
+ .toc-title {
+ font-family: 'Georgia', serif;
+ font-size: 14pt;
+ font-weight: bold;
+ color: #1a252f;
+ margin-bottom: 15px;
+ }
+ .toc ul {
+ list-style: none;
+ padding-left: 0;
+ margin: 0;
+ }
+ .toc li {
+ margin-bottom: 8px;
+ }
+ .toc-h1 { font-weight: bold; margin-top: 12px; }
+ .toc-h2 { margin-left: 20px; font-size: 10pt; color: #34495e; }
+ .toc-h3 { margin-left: 40px; font-size: 10pt; color: #7f8c8d; }
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 25px 0;
+ background-color: #ffffff;
+ }
+ th, td {
+ border: 1px solid #ecf0f1;
+ padding: 12px;
+ text-align: left;
+ }
+ th {
+ background-color: #f4f6f7;
+ color: #1a252f;
+ font-weight: bold;
+ }
+ tr:nth-child(even) {
+ background-color: #fafbfc;
+ }
+ ul, ol {
+ margin: 15px 0;
+ padding-left: 25px;
+ }
+ li {
+ margin-bottom: 10px;
+ }
+ /* Let's remove this completely for this specific case to see if it lets the table and TOC share a page */
+ /* .content-start {
+ page-break-before: always;
+ } */
+ </style>
+ """
+
+ # Inject CSS
+ html_str = html_str.replace("</head>", css + "</head>")
+
+ # Process Title Block
+ title_match = re.search(r'<RT-title\s+([^>]+)>(.*?)</RT-title>', html_str, re.DOTALL | re.IGNORECASE)
+ if title_match:
+ attrs = title_match.group(1)
+ title = re.search(r'title="(.*?)"', attrs).group(1) if 'title="' in attrs else 'Document'
+ author = re.search(r'author="(.*?)"', attrs).group(1) if 'author="' in attrs else ''
+ date = re.search(r'date="(.*?)"', attrs).group(1) if 'date="' in attrs else ''
+ copyright_txt = re.search(r'copyright="(.*?)"', attrs).group(1) if 'copyright="' in attrs else ''
+
+ title_block = f'''
+ <div class="rt-title-block">
+ <h1>{title}</h1>
+ <div class="rt-meta"><span class="author">{author}</span> | <span class="date">{date}</span></div>
+ <div class="rt-copyright">{copyright_txt}</div>
+ </div>
+ '''
+ html_str = html_str[:title_match.start()] + title_block + html_str[title_match.end():]
+
+ # Process TOC
+ toc_match = re.search(r'<RT-TOC\s+level="(.*?)"></RT-TOC>', html_str, re.IGNORECASE)
+ if toc_match:
+ level = toc_match.group(1)
+ headings = []
+ if '-' in level:
+ headings = re.findall(r'<h([123])>(.*?)</h[123]>', html_str, re.IGNORECASE)
+ else:
+ headings = re.findall(rf'<h({level})>(.*?)</h{level}>', html_str, re.IGNORECASE)
+
+ toc_html = '<div class="toc"><div class="toc-title">Table of Contents</div><ul>'
+ for lvl, text in headings:
+ # exclude COVER SHEET from TOC
+ if "COVER SHEET" in text:
+ continue
+ toc_html += f'<li class="toc-h{lvl}">{text}</li>'
+ toc_html += '</ul></div>'
+
+ html_str = html_str[:toc_match.start()] + toc_html + html_str[toc_match.end():]
+
+ # Term Replacement (First occurrence)
+ seen_terms = set()
+ def term_replace(match):
+ text = match.group(1)
+ l_text = text.lower()
+ if l_text not in seen_terms:
+ seen_terms.add(l_text)
+ return f'<span class="rt-term">{text}</span>'
+ else:
+ return f'<span class="rt-term-plain">{text}</span>'
+
+ html_str = re.sub(r'<RT-term>(.*?)</RT-term>', term_replace, html_str)
+
+ # Neologism Replacement (First occurrence)
+ seen_neos = set()
+ def neo_replace(match):
+ text = match.group(1)
+ l_text = text.lower()
+ if l_text not in seen_neos:
+ seen_neos.add(l_text)
+ return f'<span class="rt-neologism">{text}</span>'
+ else:
+ return f'<span class="rt-neologism-plain">{text}</span>'
+
+ html_str = re.sub(r'<RT-neologism>(.*?)</RT-neologism>', neo_replace, html_str)
+
+ HTML(string=html_str).write_pdf(out_file)
+
+# Locate the uploaded file
+uploaded_files = glob.glob('/mnt/data/user_uploads/*abstract.html')
+if not uploaded_files:
+ uploaded_files = glob.glob('/mnt/data/uploads/*abstract.html')
+if not uploaded_files:
+ # Look for it anywhere under /mnt/data
+ for root, dirs, files in os.walk('/mnt/data'):
+ for file in files:
+ if file == 'abstract.html':
+ uploaded_files.append(os.path.join(root, file))
+
+if uploaded_files:
+ filepath = uploaded_files[0]
+ with open(filepath, 'r', encoding='utf-8') as f:
+ html_content = f.read()
+ process_rt_html(html_content, 'abstract.pdf')
+ print("PDF successfully generated.")
+else:
+ print("Could not find abstract.html in the data directory.")
+
+Code output
+Traceback (most recent call last):
+ File "<xbox-string>", line 1, in <module>
+ import bs4
+ModuleNotFoundError: No module named 'bs4'
+
+Python
+import re
+import os
+import glob
+from weasyprint import HTML
+
+def process_rt_html(html_str, out_file):
+ html_str = html_str.replace('\\$', '$')
+ html_str = re.sub(r'<div\s+style=["\']page-break-[^>]*["\']>\s*</div>', '', html_str, flags=re.IGNORECASE)
+
+ css = """
+ <style>
+ @page {
+ size: A4;
+ margin: 25mm 20mm;
+ background-color: #faf9f6;
+ }
+ *, *::before, *::after { box-sizing: border-box; }
+ body {
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 11pt;
+ color: #2c3e50;
+ line-height: 1.6;
+ margin: 0;
+ padding: 0;
+ background-color: #faf9f6;
+ }
+ h1, h2, h3 {
+ color: #1a252f;
+ page-break-after: avoid;
+ font-family: 'Georgia', serif;
+ }
+ h1 {
+ font-size: 16pt;
+ border-bottom: 2px solid #d4af37;
+ padding-bottom: 5px;
+ margin-top: 2em;
+ }
+ h2 {
+ font-size: 14pt;
+ margin-top: 1.5em;
+ color: #2c3e50;
+ }
+ h3 {
+ font-size: 12pt;
+ margin-top: 1.2em;
+ color: #34495e;
+ }
+ .rt-title-block {
+ text-align: center;
+ margin: -25mm -20mm 30px -20mm;
+ padding: 35px 20mm;
+ background-color: #1a252f;
+ color: #ecf0f1;
+ }
+ .rt-title-block h1 {
+ color: #d4af37;
+ border: none;
+ font-size: 20pt;
+ margin: 0 0 15px 0;
+ padding: 0;
+ }
+ .rt-meta {
+ font-family: 'Georgia', serif;
+ font-size: 11pt;
+ color: #bdc3c7;
+ font-style: italic;
+ }
+ .rt-copyright {
+ font-size: 9pt;
+ color: #95a5a6;
+ margin-top: 10px;
+ }
+ .rt-term {
+ border-bottom: 1px dashed #7f8c8d;
+ font-style: italic;
+ color: #2980b9;
+ }
+ .rt-term-plain {
+ font-style: normal;
+ }
+ .rt-neologism {
+ font-weight: bold;
+ color: #c0392b;
+ }
+ .rt-neologism-plain {
+ font-weight: normal;
+ }
+ .toc {
+ background-color: #ffffff;
+ border-left: 4px solid #d4af37;
+ padding: 20px;
+ margin: 25px 0;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.05);
+ /* Ensure no forced break before TOC */
+ page-break-before: auto;
+ }
+ .toc-title {
+ font-family: 'Georgia', serif;
+ font-size: 14pt;
+ font-weight: bold;
+ color: #1a252f;
+ margin-bottom: 15px;
+ }
+ .toc ul {
+ list-style: none;
+ padding-left: 0;
+ margin: 0;
+ }
+ .toc li {
+ margin-bottom: 8px;
+ }
+ .toc-h1 { font-weight: bold; margin-top: 12px; }
+ .toc-h2 { margin-left: 20px; font-size: 10pt; color: #34495e; }
+ .toc-h3 { margin-left: 40px; font-size: 10pt; color: #7f8c8d; }
+ table {
+ width: 100%;
+ border-collapse: collapse;
+ margin: 25px 0;
+ background-color: #ffffff;
+ }
+ th, td {
+ border: 1px solid #ecf0f1;
+ padding: 12px;
+ text-align: left;
+ }
+ th {
+ background-color: #f4f6f7;
+ color: #1a252f;
+ font-weight: bold;
+ }
+ tr:nth-child(even) {
+ background-color: #fafbfc;
+ }
+ ul, ol {
+ margin: 15px 0;
+ padding-left: 25px;
+ }
+ li {
+ margin-bottom: 10px;
+ }
+ .content-start {
+ page-break-before: always;
+ }
+ </style>
+ """
+
+ html_str = html_str.replace("</head>", css + "</head>")
+
+ title_match = re.search(r'<RT-title\s+([^>]+)>(.*?)</RT-title>', html_str, re.DOTALL | re.IGNORECASE)
+ if title_match:
+ attrs = title_match.group(1)
+ title = re.search(r'title="(.*?)"', attrs).group(1) if 'title="' in attrs else 'Document'
+ author = re.search(r'author="(.*?)"', attrs).group(1) if 'author="' in attrs else ''
+ date = re.search(r'date="(.*?)"', attrs).group(1) if 'date="' in attrs else ''
+ copyright_txt = re.search(r'copyright="(.*?)"', attrs).group(1) if 'copyright="' in attrs else ''
+
+ title_block = f'''
+ <div class="rt-title-block">
+ <h1>{title}</h1>
+ <div class="rt-meta"><span class="author">{author}</span> | <span class="date">{date}</span></div>
+ <div class="rt-copyright">{copyright_txt}</div>
+ </div>
+ '''
+ html_str = html_str[:title_match.start()] + title_block + html_str[title_match.end():]
+
+ toc_match = re.search(r'<RT-TOC\s+level="(.*?)"></RT-TOC>', html_str, re.IGNORECASE)
+ if toc_match:
+ level = toc_match.group(1)
+ headings = []
+ if '-' in level:
+ headings = re.findall(r'<h([123])>(.*?)</h[123]>', html_str, re.IGNORECASE)
+ else:
+ headings = re.findall(rf'<h({level})>(.*?)</h{level}>', html_str, re.IGNORECASE)
+
+ toc_html = '<div class="toc"><div class="toc-title">Table of Contents</div><ul>'
+ for lvl, text in headings:
+ if "COVER SHEET" in text:
+ continue
+ toc_html += f'<li class="toc-h{lvl}">{text}</li>'
+ toc_html += '</ul></div>'
+
+ # Add page break AFTER the TOC instead of before it
+ toc_html += '<div class="content-start"></div>'
+
+ html_str = html_str[:toc_match.start()] + toc_html + html_str[toc_match.end():]
+
+ seen_terms = set()
+ def term_replace(match):
+ text = match.group(1)
+ l_text = text.lower()
+ if l_text not in seen_terms:
+ seen_terms.add(l_text)
+ return f'<span class="rt-term">{text}</span>'
+ else:
+ return f'<span class="rt-term-plain">{text}</span>'
+
+ html_str = re.sub(r'<RT-term>(.*?)</RT-term>', term_replace, html_str)
+
+ seen_neos = set()
+ def neo_replace(match):
+ text = match.group(1)
+ l_text = text.lower()
+ if l_text not in seen_neos:
+ seen_neos.add(l_text)
+ return f'<span class="rt-neologism">{text}</span>'
+ else:
+ return f'<span class="rt-neologism-plain">{text}</span>'
+
+ html_str = re.sub(r'<RT-neologism>(.*?)</RT-neologism>', neo_replace, html_str)
+
+ HTML(string=html_str).write_pdf(out_file)
+
+uploaded_files = glob.glob('/mnt/data/user_uploads/*abstract.html')
+if not uploaded_files:
+ uploaded_files = glob.glob('/mnt/data/uploads/*abstract.html')
+if not uploaded_files:
+ for root, dirs, files in os.walk('/mnt/data'):
+ for file in files:
+ if file == 'abstract.html':
+ uploaded_files.append(os.path.join(root, file))
+
+if uploaded_files:
+ filepath = uploaded_files[0]
+ with open(filepath, 'r', encoding='utf-8') as f:
+ html_content = f.read()
+
+ # One last aggressive check to ensure we absolutely strip ANY style="page-break..." strings
+ # in case the earlier regex missed a specific formatting
+ html_content = re.sub(r'style="[^"]*page-break-after:\s*always;?[^"]*"', '', html_content, flags=re.IGNORECASE)
+
+ process_rt_html(html_content, 'abstract_no_gap.pdf')
+ print("PDF successfully generated.")
+else:
+ print("Could not find abstract.html in the data directory.")
+++ /dev/null
-// block_visibility_during_layout.js
-
-// 1. Hide the document immediately upon execution in the <head>
-document.documentElement.style.visibility = "hidden";
-
-// 2. Define the restoration function
-const restore_visibility = function() {
- document.documentElement.style.visibility = "";
- document.removeEventListener("RT_layout_complete", restore_visibility);
- window.removeEventListener("load", restore_visibility);
-};
-
-// 3. Listen for a specific completion signal from the layout engine
-document.addEventListener("RT_layout_complete", restore_visibility);
-
-// 4. Structural Safety Net: If the layout engine fails or is never loaded,
-// restore visibility on the final window 'load' event so the page doesn't remain blank.
-window.addEventListener("load", restore_visibility);
-
+++ /dev/null
-window.RT = window.RT || {};
-
-window.RT.load = function(module_path){
- let target_module = module_path;
-
- if(target_module === 'theme'){
- let saved_theme = localStorage.getItem('RT_theme_preference');
- if(!saved_theme){
- saved_theme = 'dark_gold';
- localStorage.setItem('RT_theme_preference', saved_theme);
- }
- target_module = 'theme/' + saved_theme;
- }
-
- let resolved_path = window.RT.dirpr_library + '/' + target_module;
-
- if(!resolved_path.endsWith('.js')){
- resolved_path = resolved_path + '.js';
- }
-
- document.write('<script src="' + resolved_path + '"></script>');
-};
+++ /dev/null
-/*
- General utilities for the RT Style library.
-*/
-
-window.RT = window.RT || {};
-
-// --- DEBUG SYSTEM ---
-window.RT.debug = {
-
- // all debug messages enabled
-/*
- active_tokens: new Set([
- 'style', 'layout', 'pagination'
- ,'selector', 'config', 'error'
- ,'term'
- ,'scroll'
- ]),
-
- active_tokens: new Set([
- 'term'
- ]),
-*/
-
- active_tokens: new Set([
- ]),
-
- log: function(token, message) {
- if (this.active_tokens.has(token)) {
- console.log(`[RT:${token}]`, message);
- }
- },
-
- warn: function(token, message) {
- if (this.active_tokens.has(token)) {
- console.warn(`[RT:${token}]`, message);
- }
- },
-
- error: function(token, message) {
- console.error(`[RT:${token}] CRITICAL:`, message);
- },
-
- enable: function(token) { this.active_tokens.add(token); console.log(`Enabled: ${token}`); },
- disable: function(token) { this.active_tokens.delete(token); console.log(`Disabled: ${token}`); }
-};
-
-// --- UTILITIES ---
-window.RT.utility = {
- // --- FONT PHYSICS ---
- measure_ink_ratio: function(target_font, ref_font = null) {
- const debug = window.RT.debug;
- debug.log('layout', `Measuring ink ratio for ${target_font}`);
-
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
-
- if (!ref_font) {
- const bodyStyle = window.getComputedStyle(document.body);
- ref_font = bodyStyle.fontFamily;
- }
-
- const get_metrics = (font) => {
- ctx.font = '100px ' + font;
- const metrics = ctx.measureText('M');
- return {
- ascent: metrics.actualBoundingBoxAscent,
- descent: metrics.actualBoundingBoxDescent
- };
- };
-
- const ref_m = get_metrics(ref_font);
- const target_m = get_metrics(target_font);
-
- const ratio = ref_m.ascent / target_m.ascent;
-
- return {
- ratio: ratio,
- baseline_diff: ref_m.descent - target_m.descent
- };
- },
-
- // --- COLOR PHYSICS ---
- is_color_light: function(color_string) {
- // 1. HSL Check
- if (color_string.startsWith('hsl')) {
- const numbers = color_string.match(/\d+/g);
- if (numbers && numbers.length >= 3) {
- const lightness = parseInt(numbers[2]);
- return lightness > 50;
- }
- }
-
- // 2. RGB Check
- const rgb = color_string.match(/\d+/g);
- if (!rgb) {
- return true;
- }
-
- const r = parseInt(rgb[0]);
- const g = parseInt(rgb[1]);
- const b = parseInt(rgb[2]);
- const luma = (r * 299 + g * 587 + b * 114) / 1000;
- return luma > 128;
- },
-
- is_block_content: function(element) {
- return element.textContent.trim().includes('\n');
- }
-};
+++ /dev/null
-/home/Thomas/subu_data/developer/project/RT-style-JS_public/shared/linked-project/RT-style-JS_public/consumer/release/RT
\ No newline at end of file
+++ /dev/null
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>RT Style System: Reference Manual</title>
-
- <script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT";
- document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
- </script>
- <script>
- window.RT.load('core/utility');
- window.RT.load('core/block_visibility_during_layout');
- window.RT.load('theme');
- window.RT.load('layout/article_tech_ref');
- </script>
- </head>
-
- <body>
-
- <RT-article>
- <RT-title author="Gemini" date="2026-06-17 15:19:00Z" title="RT Style System: Reference Manual"></RT-title>
-
- <RT-TOC level="1"></RT-TOC>
-
- <RT-chapter>Table of custom tags</RT-chapter>
-
- <h2>Style tag reference</h2>
-
- <table>
- <thead>
- <tr>
- <th>Tag</th>
- <th>Description</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td><RT-code><RT-article></RT-code></td>
- <td>Article container.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-title></RT-code></td>
- <td>
- Title block and metadata.
- </td>
- </tr>
-
- <tr>
- <td><RT-code><RT-TOC></RT-code></td>
- <td>Compiles TOC.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-index></RT-code></td>
- <td>Compiles an alphabetical glossary of defined terms.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-term></RT-code></td>
- <td>Conventional term.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-term-em></RT-code></td>
- <td>Forces emphasis for a term even after first occurrence.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-neologism></RT-code></td>
- <td>Project specific term.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-neologism-em></RT-code></td>
- <td>Forces emphasis for a neologism even after first occurrence.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-code></RT-code></td>
- <td>Code span or code block.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-math></RT-code></td>
- <td>Inline or block math.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-footnote></RT-code></td>
- <td>Inline text to be extracted and rendered at the bottom of the page.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-cite></RT-code></td>
- <td>Bibliographic citation marker.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-ref></RT-code></td>
- <td>Dynamic cross reference to another element in the document.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-page></RT-code></td>
- <td>Automatically inserted pagination container.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-chapter></RT-code></td>
- <td>Chapter heading with implied page break.</td>
- </tr>
-
- <tr>
- <td><RT-code><RT-page-break></RT-code></td>
- <td>Explicit page break directive. Must be explicitly closed.</td>
- </tr>
- </tbody>
- </table>
-
- <RT-chapter>Architecture overview</RT-chapter>
- <p>
- The <RT-term>RT Style System</RT-term> is a client-side, JavaScript-driven publishing framework designed to turn raw HTML into high-readability technical documentation. Unlike standard CSS frameworks, RT uses JavaScript to handle complex layout tasks like <RT-neologism>ink-ratio balancing</RT-neologism> and dynamic pagination.
- </p>
-
- <h2>Pulling style files into a document</h2>
- <p> Put `setup.js` in the directory, and include it at the top of the document,
- `setup.js` points at the style files and is installation specific.</p>
-
-
- <RT-chapter>Semantic tags</RT-chapter>
- <p>
- The system relies on a specific set of custom tags in the <RT-code>RT-</RT-code> namespace to separate meaning from presentation.
- </p>
-
- <h2>Terminology</h2>
-
- <h3>Conventional terms</h3>
- <p>
- Use <RT-code><RT-term></RT-code> for standard, industry accepted technical terms. The system decorates only the first occurrence of each unique term so that the first appearance functions as a definition point, and later appearances do not overload the page with styling.
- </p>
-
- <RT-code>
- The <RT-term>Singleton Pattern</RT-term> restricts instantiation...
- </RT-code>
-
- <p>
- <em>Renders as:</em> The <RT-term>Singleton Pattern</RT-term> restricts instantiation...
- </p>
-
- <h3>Neologisms</h3>
- <p>
- Use <RT-code><RT-neologism></RT-code> for terms invented specifically for the current document or project. Neologisms are styled more strongly than conventional terms, visually distinguishing "jargon you should know" from "jargon we invented."
- </p>
-
- <RT-code>
- We define the <RT-neologism>Hyper Tape</RT-neologism> as a construct...
- </RT-code>
-
- <p>
- <em>Renders as:</em> We define the <RT-neologism>Hyper Tape</RT-neologism> as a construct...
- </p>
-
- <h3>First occurrence rule</h3>
- <p>
- The term system is intentionally conservative. For the tags <RT-code><RT-term></RT-code> and <RT-code><RT-neologism></RT-code>, only the first occurrence of a unique term is decorated. Subsequent mentions are rendered as normal prose.
- </p>
-
- <p>
- Uniqueness is tracked by normalizing the term text (trimmed, then lowercased). This means that <RT-code>Symbol</RT-code> and <RT-code>symbol</RT-code> count as the same term.
- </p>
-
- <h3>Forced emphasis variants</h3>
- <p>
- Sometimes a later mention of a term should be emphasized again. For that purpose, the system provides explicit emphasis tags:
- </p>
-
- <ul>
- <li><RT-code><RT-term-em></RT-code></li>
- <li><RT-code><RT-neologism-em></RT-code></li>
- </ul>
-
- <p>
- These variants are always decorated, even if the term appeared earlier.
- </p>
-
- <h3>Automatic definition anchors</h3>
- <p>
- For first occurrences, the term module automatically assigns an <RT-code>id</RT-code> attribute if one does not already exist. This establishes stable anchors for future indexing and linking.
- </p>
-
- <RT-code>
- We define a <RT-term>Symbol</RT-term> as...
- </RT-code>
-
- <p>
- The first occurrence will be given an id similar to <RT-code>def-symbol</RT-code>. For neologisms, an additional marker is used, for example <RT-code>def-neo-hyper-tape</RT-code>.
- </p>
-
- <h2>Technical content</h2>
-
- <h3>Code</h3>
- <p>
- Use <RT-code><RT-code></RT-code>. If placed inline, it acts like a span. If placed as a block (with newlines), it acts like a pre formatted block with a theme aware border.
- </p>
-
- <RT-code>
- # Block Code Example
- def hello():
- return "World"
- </RT-code>
-
- <h3>Mathematics</h3>
- <p>
- Use <RT-code><RT-math></RT-code>. The system auto detects if it is a block equation or inline variable and wraps it in MathJax delimiters.
- </p>
- <p>
- Inline: Let <RT-math>x</RT-math> be the input.
- </p>
- <p>
- Block:
- </p>
- <RT-math>
- f(x) = \sum_{i=0}^{n} x_i
- </RT-math>
-
- <h2>References, citations, and indexing</h2>
-
- <h3>Footnotes</h3>
- <p>
- Use <RT-code><RT-footnote></RT-code> to author footnote content directly inline with the text it references. During the layout phase, the pagination engine extracts the text, replaces the element with an invisible structural marker, and compiles a formatted footnote container at the bottom of the active <RT-code><RT-page></RT-code>.
- </p>
-
- <h3>Citations</h3>
- <p>
- Use <RT-code><RT-cite></RT-code> with the <RT-code>ref</RT-code> attribute to insert bibliographic citations. This ensures formatting remains consistent and separates citation data from standard prose.
- </p>
- <RT-code>
- ... formalized as Basic Law V <RT-endnote ref="Gottlob Frege, Grundgesetze, 1903"></RT-endnote>.
- </RT-code>
-
- <h3>Dynamic cross referencing</h3>
- <p>
- A person authoring a technical document frequently refers back to tables, equations, or previous sections. Use <RT-code><RT-ref target="element-id"></RT-code> to link to specific content. This tag dynamically retrieves the numbering or caption of the target element, maintaining correct references even if preceding elements shift during editing.
- </p>
-
- <h3>Glossary indexing</h3>
- <p>
- Use <RT-code><RT-index></RT-code> at the end of the document to compile a glossary. Similar to the TOC compiler, this tag scans the DOM for automatic definition anchors produced by the term module and generates an alphabetized list of defined terms and neologisms.
- </p>
-
- <RT-chapter>Navigation and layout</RT-chapter>
-
- <h2>Automatic table of contents</h2>
- <p>
- Use <RT-code><RT-TOC></RT-code> to insert a compiled table of contents. The tag scans the document <em>forward</em> from its current position to collect headings.
- </p>
-
- <h3>Explicit mode</h3>
- <p>
- Use the <RT-code>level="N"</RT-code> attribute to target a specific heading depth.
- </p>
- <ul>
- <li><RT-code><RT-TOC level="1"></RT-code>: Collects all <RT-code><h1></RT-code> elements until the end of the document. Best for the main document index.</li>
- <li><RT-code><RT-TOC level="2"></RT-code>: Collects all <RT-code><h2></RT-code> elements until it encounters the next <RT-code><h1></RT-code>. Best for chapter summaries.</li>
- </ul>
-
- <h3>Implicit mode</h3>
- <p>
- If no level is specified, the TOC scans backwards to find the nearest heading (for example H1) and assumes you want to collect children one level deeper (for example H2).
- </p>
- <p>
- <em>Note: Implicit mode can fail if placed before the first heading of a section. Use explicit levels for robust results.</em>
- </p>
-
- <h2>The title block</h2>
- <p>
- Use <RT-code><RT-title></RT-code> as the first element in your <RT-code><body></RT-code> (before the article container). This tag produces a standardized, styled header block with the document title and metadata.
- </p>
-
- <h3>Attributes</h3>
- <ul>
- <li><RT-code>title</RT-code> (Required): The main heading of the document.</li>
- <li><RT-code>author</RT-code> (Optional): The author's name. Renders in a bold accent color.</li>
- <li><RT-code>date</RT-code> (Optional): The publication or revision date.</li>
- </ul>
-
- <h3>Example</h3>
- <RT-code>
- <RT-title
- title="RT Style System: Reference Manual"
- author="Gemini"
- date="2026-06-17 15:19:00Z">
- </RT-title>
- </RT-code>
-
- <p>
- <em>Renders as:</em> A centered, high contrast H1 followed by a serif styled metadata row containing the author and date.
- </p>
-
- <h2>The article container</h2>
- <p>
- The root element must be <RT-code><RT-article></RT-code>. This is the boundary for the pagination logic.
- </p>
-
- <h2>Pagination</h2>
- <p>
- The script <RT-code>paginate_by_element.js</RT-code> scans the article. It calculates the height of every element (including margins) and bundles them into <RT-code><RT-page></RT-code> elements.
- </p>
- <p>
- <RT-neologism>Soft Limit Pagination</RT-neologism>: The system attempts to keep headers with their following paragraphs. It will break a page early rather than stranding a header at the bottom.
- </p>
-
- <h2>Manual page breaks and chapters</h2>
- <p>
- A person can assert explicit control over the layout engine using the <RT-code><RT-page-break></RT-page-break></RT-code> tag. This tag forces the engine to close the current page and push all subsequent content to the top of the next page. Standard HTML5 parsing requires this custom tag to be explicitly closed; self-closing syntax will fail.
- </p>
- <p>
- For major document divisions, use <RT-code><RT-chapter></RT-code>. The semantic processor translates this tag into an <RT-code><RT-page-break></RT-code> followed by an <RT-code><h1 class="RT-chapter"></RT-code>. This ensures the chapter starts on a new page, inherits standard typography, and is automatically indexed by the TOC engine.
- </p>
-
- <RT-chapter>Debugging</RT-chapter>
-
- <h2>Debug tokens</h2>
- <p>
- RT provides a lightweight debug logging system in <RT-code>utility.js</RT-code>. Logging is controlled by a set of active debug tokens. Each log message is assigned a token, and the message prints only if that token is enabled.
- </p>
-
- <p>
- Examples of common tokens include <RT-code>style</RT-code>, <RT-code>layout</RT-code>, <RT-code>pagination</RT-code>, <RT-code>selector</RT-code>, <RT-code>config</RT-code>, and <RT-code>term</RT-code>.
- </p>
-
- <h2>How logging is gated</h2>
- <p>
- Normal log and warning output are gated. The methods <RT-code>debug.log(token,message)</RT-code> and <RT-code>debug.warn(token,message)</RT-code> will print only when the token exists in <RT-code>debug.active_tokens</RT-code>.
- </p>
-
- <p>
- This prevents the console from being flooded during normal use, while still allowing deep visibility during development.
- </p>
-
- <h2>Errors are always printed</h2>
- <p>
- Errors are treated differently. The method <RT-code>debug.error(token,message)</RT-code> always prints, regardless of token state. These messages represent failures that require attention.
- </p>
-
- <h2>Enabling and disabling tokens</h2>
- <p>
- Tokens are enabled or disabled in two ways: by editing the <RT-code>active_tokens</RT-code> set in <RT-code>utility.js</RT-code>, or at runtime by calling:
- </p>
-
- <RT-code>
- window.RT.debug.enable('term')
- window.RT.debug.disable('term')
- </RT-code>
-
- <p>
- For example, the term system (<RT-code>RT_term.js</RT-code>) uses the <RT-code>term</RT-code> token. When that token is enabled, the module will print messages describing how terms were detected and which term definitions were assigned IDs.
- </p>
-
- <RT-chapter>Themes</RT-chapter>
- <p>
- The system supports hot swapping themes by changing the script import in the head.
- </p>
- <ul>
- <li><strong>Dark:</strong> <RT-code>style/theme_dark_gold.js</RT-code> (Default)</li>
- <li><strong>Light:</strong> <RT-code>style/theme_light_gold.js</RT-code></li>
- </ul>
-
- <RT-chapter>Manifest</RT-chapter>
- <RT-code>
- 2. utility.js (Math/Color physics)
- 3. article_tech_ref.js (Typography and CSS injection)
- 4. RT_code.js (Code block logic)
- 5. RT_math.js (MathJax wrapper)
- 6. RT_term.js (Term styling)
- 7. RT_TOC.js (Navigation generator)
- 8. paginate_by_element.js (Page splitter)
- 9. page_fixed_glow.js (Visual page container)
- </RT-code>
-
- <RT-chapter>RT conventions</RT-chapter>
- <p> Headings are first letter capitalized. Remaining words are as they would be in English prose.</p>
-
- <RT-chapter>Exercises</RT-chapter>
-
- <ol>
- <li>
- <p>
- <strong>Term occurrences:</strong> If an author tags a word with <RT-code><RT-term></RT-code> five times in a document, how many times will the term be visually decorated by the styling engine? How does a person force the engine to decorate a later occurrence?
- </p>
- </li>
- <li>
- <p>
- <strong>Term normalization:</strong> A person writes <RT-code><RT-term>Parse Tree</RT-term></RT-code> in paragraph one, and <RT-code><RT-term>parse tree</RT-term></RT-code> in paragraph two. Will the system assign a definition anchor to the second occurrence?
- </p>
- </li>
- <li>
- <p>
- <strong>TOC compilation:</strong> What happens if a person places an <RT-code><RT-TOC></RT-code> tag without a level attribute before the first heading of a section?
- </p>
- </li>
- <li>
- <p>
- <strong>Code and math formatting:</strong> How does the RT system determine whether to render an <RT-code><RT-code></RT-code> or <RT-code><RT-math></RT-code> element inline with the text versus as a standalone block?
- </p>
- </li>
- <li>
- <p>
- <strong>Pagination logic:</strong> When the script bundles elements into an <RT-code><RT-page></RT-code> container, how does the soft limit pagination handle a heading that falls at the very bottom of the page capacity?
- </p>
- </li>
- </ol>
-
- </RT-article>
- </body>
-</html>
+++ /dev/null
-/*
- Processes <RT-TOC> tags.
- Populates each with headings found below it.
-
- Attributes:
- level="N" : Explicitly sets the target heading level (1-6).
- e.g., level="1" collects H1s. level="2" collects H2s.
- Stops collecting if it hits a heading of (level - 1) or higher.
-
- Default (No attribute):
- Context Aware. Looks backwards for the nearest heading H(N).
- Targets H(N+1). Stops at the next H(N).
-
-First heading 1 1
- First heading 2 2
- Next heading 2 2
-Next heading 2 3
-
-*/
-
-window.RT = window.RT || {};
-
-window.RT.TOC = function(){
- const debug = window.RT.debug || { log: function(){} };
- const TOC_seq = document.querySelectorAll('rt-toc');
-
- TOC_seq.forEach( (container ,TOC_index) => {
- container.style.display = 'block';
-
- // 1. Parse attribute: single number N or range A-B
- const attr_val = container.getAttribute('level');
- let start_level, end_level;
-
- if (attr_val) {
- const rangeMatch = attr_val.match(/^(\d)-(\d)$/);
- if (rangeMatch) {
- const a = parseInt(rangeMatch[1]);
- const b = parseInt(rangeMatch[2]);
- if (a >= 1 && a <= 6 && b >= 1 && b <= 6 && a <= b) {
- start_level = a;
- end_level = b;
- if (debug.log) debug.log('TOC', `TOC #${TOC_index} range: H${a}-H${b}`);
- } else {
- if (debug.log) debug.log('TOC', `Invalid range "${attr_val}" → implicit mode`);
- }
- } else {
- const single = parseInt(attr_val);
- if (!isNaN(single) && single >= 1 && single <= 6) {
- start_level = single;
- end_level = single;
- if (debug.log) debug.log('TOC', `TOC #${TOC_index} single level: H${single}`);
- } else {
- if (debug.log) debug.log('TOC', `Invalid level "${attr_val}" → implicit mode`);
- }
- }
- }
-
- // 2. Implicit mode (no attribute or invalid)
- if (start_level === undefined || end_level === undefined) {
- let context_level = 0;
- let prev = container.previousElementSibling;
- while (prev) {
- const match = prev.tagName.match(/^H([1-6])$/);
- if (match) {
- context_level = parseInt(match[1]);
- break;
- }
- prev = prev.previousElementSibling;
- }
- const target_level = Math.min(context_level + 1, 6);
- start_level = target_level;
- end_level = target_level;
- if (debug.log) debug.log('TOC', `TOC #${TOC_index} implicit target: H${target_level}`);
- }
-
- // 3. Collect all matching headings until a higher-level heading stops us
- const headings = [];
- let next_el = container.nextElementSibling;
- while (next_el) {
- const match = next_el.tagName.match(/^H([1-6])$/);
- if (match) {
- const found_level = parseInt(match[1]);
-
- // Stop if we hit a heading that is a parent of the lowest level we collect
- if (found_level < start_level) break;
-
- // Collect if within the requested range
- if (found_level >= start_level && found_level <= end_level) {
- // Ensure it has an id
- if (!next_el.id) {
- next_el.id = `TOC-ref-${TOC_index}-${found_level}-${headings.length}`;
- }
- headings.push({ el: next_el, level: found_level });
- }
- }
- next_el = next_el.nextElementSibling;
- }
-
- // 4. Build the container (title + list)
- container.innerHTML = '';
- const title = document.createElement('h1');
- title.textContent = start_level === 1 ? 'Table of Contents' : 'Section Contents';
- title.style.textAlign = 'center';
- container.appendChild(title);
-
- if (headings.length === 0) return; // nothing to show
-
- // Top-level list
- const topList = document.createElement('ul');
- topList.style.listStyle = 'none';
- topList.style.paddingLeft = '0';
- container.appendChild(topList);
-
- // Stack of <ul> elements; index 0 = top-level list
- const listStack = [topList];
-
- for (const item of headings) {
- // Depth relative to start_level
- const depth = item.level - start_level; // 0 = top-level, 1 = sub-level, etc.
-
- // Ensure we have the correct nesting depth
- while (listStack.length - 1 > depth) {
- // Pop until we are at the right depth
- listStack.pop();
- }
-
- // If we need to go deeper, open new sub-lists inside the last <li>
- while (listStack.length - 1 < depth) {
- const parentList = listStack[listStack.length - 1];
- const lastLi = parentList.lastElementChild;
- if (lastLi) {
- const subList = document.createElement('ul');
- subList.style.listStyle = 'none';
- subList.style.paddingLeft = '1.5rem'; // indentation for nested items
- lastLi.appendChild(subList);
- listStack.push(subList);
- } else {
- // No parent <li> yet – stay at current depth (flatten)
- break;
- }
- }
-
- // Create the <li> for this heading
- const li = document.createElement('li');
- li.style.marginBottom = '0.5rem';
-
- const a = document.createElement('a');
- a.href = `#${item.el.id}`;
- a.textContent = item.el.textContent;
- a.style.textDecoration = 'none';
- a.style.color = 'inherit';
- a.style.display = 'block';
-
- a.onmouseover = () => a.style.color = 'var(--rt-brand-primary)';
- a.onmouseout = () => a.style.color = 'inherit';
-
- li.appendChild(a);
- // Add to the current deepest list
- listStack[listStack.length - 1].appendChild(li);
- }
- });
-};
+++ /dev/null
-/*
- Processes <RT-chapter> tags.
- Transforms the tag into an <RT-page-break> followed by an <h1> with the RT-chapter class.
-*/
-window.RT = window.RT || {};
-
-window.RT.chapter = function(){
- const debug = window.RT.debug || { log: function(){} };
-
- document.querySelectorAll('RT-chapter').forEach( (el ,index) => {
- if(debug.log) debug.log('chapter' ,`Processing chapter ${index + 1}`);
-
- const brk = document.createElement('RT-page-break');
- const h1 = document.createElement('h1');
-
- h1.innerHTML = el.innerHTML;
-
- if(el.className){
- h1.className = el.className;
- }
- h1.classList.add('RT-chapter');
-
- Array.from(el.attributes).forEach( (attr) => {
- if(attr.name !== 'class'){
- h1.setAttribute(attr.name ,attr.value);
- }
- });
-
- el.parentNode.insertBefore(brk ,el);
- el.replaceWith(h1);
- });
-};
+++ /dev/null
-/*
- Processes <RT-CODE> tags.
- Uses the central config or CSS variables from the theme.
-
- Removes common indent from lines of code.
-*/
-function code() {
- const RT = window.RT;
- const U = RT.utility;
- const debug = RT.debug;
-
- debug.log('code', 'Starting render cycle.');
-
- const metrics = U.measure_ink_ratio('monospace');
-
- document.querySelectorAll('rt-code').forEach((el) => {
- el.style.fontFamily = 'monospace';
-
- const computed = window.getComputedStyle(el);
- const accent = computed.getPropertyValue('--rt-accent').trim() || 'gold';
-
- const is_block = U.is_block_content(el);
- const parentColor = computed.color;
- const is_text_light = U.is_color_light(parentColor);
-
- const alpha = is_block ? 0.08 : 0.15;
- const overlay = is_text_light ? `rgba(255,255,255,${alpha})` : `rgba(0,0,0,${alpha})`;
- const text_color = is_text_light ? '#ffffff' : '#000000';
-
- el.style.backgroundColor = overlay;
-
- if (is_block) {
- el.style.display = 'block';
-
- // --- Tag-Relative Auto-Dedent Logic ---
-
- // 1. Get Tag Indentation (The Anchor)
- let tagIndent = '';
- const prevNode = el.previousSibling;
- if (prevNode && prevNode.nodeType === 3) {
- const prevText = prevNode.nodeValue;
- const lastNewLineIndex = prevText.lastIndexOf('\n');
- if (lastNewLineIndex !== -1) {
- tagIndent = prevText.substring(lastNewLineIndex + 1);
- } else if (/^\s*$/.test(prevText)) {
- tagIndent = prevText;
- }
- }
-
- // 2. Calculate Common Leading Whitespace from Content
- const rawLines = el.textContent.split('\n');
-
- // Filter out empty lines for calculation purposes so they don't break the logic
- const contentLines = rawLines.filter(line => line.trim().length > 0);
-
- let commonIndent = null;
-
- if (contentLines.length > 0) {
- // Assume the first line sets the standard
- const firstMatch = contentLines[0].match(/^\s*/);
- commonIndent = firstMatch ? firstMatch[0] : '';
-
- // Reduce the commonIndent if subsequent lines have LESS indentation
- for (let i = 1; i < contentLines.length; i++) {
- const line = contentLines[i];
- // Determine how much of commonIndent this line shares
- let j = 0;
- while (j < commonIndent.length && j < line.length && commonIndent[j] === line[j]) {
- j++;
- }
- commonIndent = commonIndent.substring(0, j);
- if (commonIndent.length === 0) break; // Optimization
- }
- } else {
- commonIndent = '';
- }
-
- // 3. Process Content
- // Rule: Only strip if the Common Indent contains the Tag Indent (Safety Check)
- // This handles the Emacs case: Tag is " ", Common is " ". " " starts with " ".
- // We strip " ", leaving the code flush left.
- let finalString = '';
-
- if (commonIndent.length > 0 && commonIndent.startsWith(tagIndent)) {
- const cleanedLines = rawLines.map(line => {
- // Strip the common indent from valid lines
- return line.startsWith(commonIndent) ? line.replace(commonIndent, '') : line;
- });
-
- // Remove artifact lines (first/last empty lines)
- if (cleanedLines.length > 0 && cleanedLines[0].length === 0) {
- cleanedLines.shift();
- }
- if (cleanedLines.length > 0 && cleanedLines[cleanedLines.length - 1].trim().length === 0) {
- cleanedLines.pop();
- }
- finalString = cleanedLines.join('\n');
- } else {
- // Fallback: Code is to the left of the tag or weirdly formatted.
- // Just trim the wrapper newlines.
- finalString = el.textContent.trim();
- }
-
- el.textContent = finalString;
- // --- End Indentation Logic ---
-
- el.style.whiteSpace = 'pre';
- el.style.fontSize = (parseFloat(computed.fontSize) * metrics.ratio * 0.95) + 'px';
- el.style.padding = '1.2rem';
- el.style.margin = '1.5rem 0';
- el.style.borderLeft = `4px solid ${accent}`;
- el.style.color = 'inherit';
- } else {
- el.style.display = 'inline';
- const exactPx = parseFloat(computed.fontSize) * metrics.ratio * 1.0;
- el.style.fontSize = exactPx + 'px';
- el.style.padding = '0.1rem 0.35rem';
- el.style.borderRadius = '3px';
- const offsetPx = metrics.baseline_diff * (exactPx / 100);
- el.style.verticalAlign = offsetPx + 'px';
- el.style.color = text_color;
- }
- });
-
- debug.log('code', 'Render cycle complete.');
-}
-
-window.RT = window.RT || {};
-window.RT.code = code;
+++ /dev/null
-// developer/authored/RT/element/constraint.js
-window.RT = window.RT || {};
-
-window.RT.constraint = function(){
- document.querySelectorAll('rt-constraint').forEach( (el) => {
- el.style.display = 'block';
- el.style.borderLeft = '4px solid var(--rt-state-warning)';
- el.style.backgroundColor = 'var(--rt-surface-1)';
- el.style.padding = '1rem';
- el.style.margin = '1.5rem 0';
- el.style.color = 'var(--rt-content-main)';
- });
-};
+++ /dev/null
-// developer/authored/RT/element/crossref.js
-window.RT = window.RT || {};
-
-window.RT.crossref = function(){
- document.querySelectorAll('rt-crossref').forEach( (el) => {
- el.style.color = 'var(--rt-brand-link)';
- el.style.textDecoration = 'underline';
- el.style.cursor = 'pointer';
- el.style.fontWeight = '500';
-
- // Note: To make this fully context-aware across soft limits,
- // this module will eventually need to hook into the page
- // registry built by paginate_by_element.js.
- });
-};
+++ /dev/null
-window.RT = window.RT || {};
-
-window.RT.end_note = function(){
- const citations = document.querySelectorAll('rt-cite');
- if(citations.length === 0) return;
-
- const article = document.querySelector('rt-article');
- if(!article) return;
-
- // 1. Ensure the H1 is a direct child of the article so the TOC can see it
- let endnotesHeader = document.getElementById('endnotes-header');
- if (!endnotesHeader) {
- endnotesHeader = document.createElement('h1');
- endnotesHeader.id = 'endnotes-header';
- endnotesHeader.innerText = 'Endnotes';
- article.appendChild(endnotesHeader);
- }
-
- // 2. Locate or generate the endnotes list container
- let endnoteContainer = document.querySelector('rt-endnotes');
- if(!endnoteContainer) {
- endnoteContainer = document.createElement('rt-endnotes');
- article.appendChild(endnoteContainer);
- }
-
- // 3. Ensure the list structure exists
- if(!endnoteContainer.querySelector('ol')) {
- endnoteContainer.innerHTML = '<ol></ol>';
- }
-
- const list = endnoteContainer.querySelector('ol');
-
- // Process each inline citation
- citations.forEach((cite, index) => {
- const refNum = index + 1;
- const refText = cite.getAttribute('ref') || cite.innerHTML;
-
- cite.innerHTML = `<a href="#note-${refNum}" id="cite-${refNum}">[${refNum}]</a>`;
- cite.style.cursor = 'pointer';
- cite.style.color = 'var(--rt-brand-link)';
- cite.style.textDecoration = 'none';
-
- // Append the corresponding entry into the endnotes list
- const li = document.createElement('li');
- li.id = `note-${refNum}`;
- li.innerHTML = `${refText} <a href="#cite-${refNum}" style="text-decoration:none;">↩</a>`;
- list.appendChild(li);
- });
-
- // Style the container
- endnoteContainer.style.display = 'block';
- endnoteContainer.style.marginTop = '1rem';
- endnoteContainer.style.borderTop = '1px solid var(--rt-surface-3)';
- endnoteContainer.style.paddingTop = '1rem';
-};
+++ /dev/null
-/*
- Processes <RT-MATH> tags.
- JavaScript: math()
- HTML Tag: <RT-MATH> (parsed as rt-math)
-*/
-function math(){
- // querySelector treats 'rt-math' as case-insensitive for the tag
- document.querySelectorAll('rt-math').forEach(el => {
- if (el.textContent.startsWith('$')) return;
-
- const is_block = el.parentElement.tagName === 'DIV' ||
- el.textContent.includes('\n') ||
- el.parentElement.childNodes.length === 1;
-
- const delimiter = is_block ? '$$' : '$';
- el.style.display = is_block ? 'block' : 'inline';
- el.textContent = `${delimiter}${el.textContent.trim()}${delimiter}`;
- });
-
- // MathJax must find its config at window.MathJax
- window.MathJax = {
- tex: {
- inlineMath: [['$', '$']],
- displayMath: [['$$', '$$']]
- }
- };
-
- const script = document.createElement('script');
- script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js';
- script.async = true;
- document.head.appendChild(script);
-}
-
-window.RT = window.RT || {};
-window.RT.math = math;
+++ /dev/null
-// developer/authored/RT/element/symbol.js
-window.RT = window.RT || {};
-
-window.RT.symbol = function(){
- document.querySelectorAll('rt-symbol').forEach( (el) => {
- el.style.fontFamily = '"Courier New", Courier, monospace';
- el.style.fontWeight = '600';
- el.style.padding = '0 0.1em';
- });
-};
+++ /dev/null
-/*
- Processes <RT-TERM> and <RT-NEOLOGISM> tags.
- - Styles only the first occurrence of a unique term/neologism.
- - The "-em" variants (e.g., <RT-term-em>) are always styled.
- - Automatically generates IDs for first occurrences for future indexing.
-*/
-
-window.RT = window.RT || {};
-
-window.RT.term = function() {
- const RT = window.RT;
-
- const debug = RT.debug || {
- log: function() {}
- ,warn: function() {}
- ,error: function() {}
- };
-
- const DEBUG_TOKEN_S = 'term';
-
- try {
- // Track seen terms so only the first occurrence is decorated
- const seen_terms_dpa = new Set();
-
- const apply_style = (el, is_neologism_b) => {
- el.style.fontStyle = 'italic';
- el.style.fontWeight = is_neologism_b ? '600' : '500';
- el.style.color = is_neologism_b
- ? 'var(--rt-brand-secondary)'
- : 'var(--rt-brand-primary)';
- el.style.paddingRight = '0.1em'; // Compensation for italic slant
- el.style.display = 'inline';
- };
-
- const clear_style = (el) => {
- el.style.fontStyle = 'normal';
- el.style.color = 'inherit';
- el.style.fontWeight = 'inherit';
- el.style.paddingRight = '';
- el.style.display = '';
- };
-
- const selector_s = [
- 'rt-term'
- ,'rt-term-em'
- ,'rt-neologism'
- ,'rt-neologism-em'
- ].join(',');
-
- const tags_dpa = document.querySelectorAll(selector_s);
-
- debug.log(DEBUG_TOKEN_S, `Scanning ${tags_dpa.length} term tags`);
-
- tags_dpa.forEach(el => {
- const tag_name_s = el.tagName.toLowerCase();
- const is_neologism_b = tag_name_s.includes('neologism');
- const is_explicit_em_b = tag_name_s.endsWith('-em');
-
- const term_text_raw_s = (el.textContent || '').trim();
- if (!term_text_raw_s.length) {
- debug.warn(DEBUG_TOKEN_S, `Empty term tag encountered: <${tag_name_s}>`);
- return;
- }
-
- // Normalize text for uniqueness tracking
- const term_norm_s = term_text_raw_s.toLowerCase();
-
- // Slug for ID generation (simple + stable)
- const slug_s = term_norm_s.replace(/\s+/g, '-');
-
- const is_first_occurrence_b = !seen_terms_dpa.has(term_norm_s);
-
- if (is_explicit_em_b || is_first_occurrence_b) {
- apply_style(el, is_neologism_b);
-
- if (!is_explicit_em_b && is_first_occurrence_b) {
- seen_terms_dpa.add(term_norm_s);
-
- if (!el.id) {
- el.id = `def-${is_neologism_b ? 'neo-' : ''}${slug_s}`;
- debug.log(
- DEBUG_TOKEN_S
- ,`First occurrence: "${term_norm_s}" -> id="${el.id}"`
- );
- } else {
- debug.log(
- DEBUG_TOKEN_S
- ,`First occurrence: "${term_norm_s}" (existing id="${el.id}")`
- );
- }
- } else if (is_explicit_em_b) {
- debug.log(
- DEBUG_TOKEN_S
- ,`Emphasized occurrence: "${term_norm_s}" (<${tag_name_s}>)`
- );
- }
- } else {
- // Subsequent mentions render as normal prose
- clear_style(el);
- }
- });
-
- debug.log(DEBUG_TOKEN_S, `Unique terms defined: ${seen_terms_dpa.size}`);
- } catch (e) {
- debug.error('error', `term failed: ${e && e.message ? e.message : String(e)}`);
- }
-};
+++ /dev/null
-class ThemeSelector extends HTMLElement{
- connectedCallback(){
- let current_theme = localStorage.getItem('RT_theme_preference');
- if(!current_theme){
- current_theme = 'dark_gold';
- }
-
- this.innerHTML = `
- <div style="position:fixed; top:10px; right:10px; z-index:1000; background:#222; padding:10px; border:1px solid #555; color: white; font-family: sans-serif;">
- <b>Theme Selection</b><br>
- <label>
- <input type="radio" name="rt-theme" value="dark_gold" ${current_theme === 'dark_gold' ? 'checked' : ''}> Dark Gold
- </label><br>
- <label>
- <input type="radio" name="rt-theme" value="light_gold" ${current_theme === 'light_gold' ? 'checked' : ''}> Light Gold
- </label>
- </div>
- `;
-
- this.addEventListener( 'change' ,(e) => {
- localStorage.setItem('RT_theme_preference' ,e.target.value);
- location.reload();
- } );
- }
-}
-
-customElements.define('rt-theme-selector' ,ThemeSelector);
+++ /dev/null
-/*
- Processes <RT-TITLE> tags.
- Generates a standard document header block.
-
- Usage:
- <RT-title title="..." author="..." date="..." copyright="..."></RT-title>
-*/
-window.RT = window.RT || {};
-
-window.RT.title = function() {
- const debug = window.RT.debug || { log: function(){} };
-
- document.querySelectorAll('rt-title').forEach(el => {
- const title = el.getAttribute('title') || 'Untitled Document';
- const author = el.getAttribute('author');
- const date = el.getAttribute('date');
- const copyright = el.getAttribute('copyright');
-
- if (debug.log) debug.log('title', `Generating title block: ${title}`);
-
- // Container
- const container = document.createElement('div');
- container.style.textAlign = 'center';
- container.style.marginBottom = '3rem';
- container.style.marginTop = '2rem';
- container.style.borderBottom = '1px solid var(--rt-border-default)';
- container.style.paddingBottom = '1.5rem';
-
- // Main Title (H1)
- const h1 = document.createElement('h1');
- h1.textContent = title;
- h1.style.margin = '0 0 0.8rem 0';
- h1.style.border = 'none'; // Override standard H1 border
- h1.style.padding = '0';
- h1.style.color = 'var(--rt-brand-primary)';
- h1.style.fontSize = '2.5em';
- h1.style.lineHeight = '1.1';
- h1.style.letterSpacing = '-0.03em';
-
- container.appendChild(h1);
-
- // Metadata Row (Author | Date)
- if (author || date) {
- const meta = document.createElement('div');
- meta.style.color = 'var(--rt-content-muted)';
- meta.style.fontStyle = 'italic';
- meta.style.fontSize = '1.1em';
- meta.style.fontFamily = '"Georgia", "Times New Roman", serif'; // Classy serif
-
- const parts = [];
- if (author) parts.push(`<span style="font-weight:600; color:var(--rt-brand-secondary)">${author}</span>`);
- if (date) parts.push(date);
-
- meta.innerHTML = parts.join(' — ');
- container.appendChild(meta);
- }
-
- // Copyright Row
- if (copyright) {
- const copy_div = document.createElement('div');
- copy_div.style.color = 'var(--rt-content-muted)';
- copy_div.style.fontSize = '0.9em';
- copy_div.style.marginTop = '0.5rem';
- // Automatically injects the copyright symbol
- copy_div.innerHTML = `© ${copyright}`;
- container.appendChild(copy_div);
- }
-
- // Replace the raw tag with the generated block
- el.replaceWith(container);
- });
-};
+++ /dev/null
-// debug messages don't work here, because core/utility isn't loaded until after the function runs.
-(function(){
- const RT = window.RT = window.RT || {};
- const debug = RT.debug || { log: function(){} };
-
- debug.log('scroll', "1. Initializing script.");
-
- // 1. Intercept native history restoration immediately
- if ('scrollRestoration' in history) {
- history.scrollRestoration = 'manual';
- debug.log('scroll', "2. history.scrollRestoration set to manual.");
- }
-
- // 2. Read coordinate from memory before any layout shifts occur
- const raw_target = sessionStorage.getItem('RT_saved_y');
- const target_y = raw_target !== null ? parseInt(raw_target, 10) : 0;
-
- // 3. Determine if the execution is a page reload
- let is_reload = false;
- if (window.performance) {
- const nav_entries = performance.getEntriesByType("navigation");
- if (nav_entries.length > 0) {
- is_reload = (nav_entries[0].type === "reload");
- } else if (performance.navigation) {
- is_reload = (performance.navigation.type === 1);
- }
- }
-
- debug.log('scroll', `3. Target Y: ${target_y} | Is Reload: ${is_reload}`);
-
- // 4. The Lock
- let is_layout_locked = true;
-
- // Helper to ensure we only signal completion once
- function unlock_layout() {
- if (!is_layout_locked) return;
- is_layout_locked = false;
- debug.log('scroll', "10. Layout fully unlocked. Emitting completion signal.");
- document.dispatchEvent(new Event("RT_layout_complete"));
- }
-
- // 5. Declare Dependencies
- RT.load('element/chapter');
- RT.load('element/endnote');
- RT.load('element/math');
- RT.load('element/code');
- RT.load('element/term');
- RT.load('element/TOC');
- RT.load('element/title');
- RT.load('element/theme_selector');
- RT.load('element/symbol');
- RT.load('element/constraint');
- RT.load('element/crossref');
-
- RT.load('layout/paginate_by_element');
- RT.load('layout/page_fixed_glow');
-
- // 6. The Typography Layout
- RT.article = function(){
- RT.config = RT.config || {};
- RT.config.article = {
- font_family: '"Noto Sans", "Segoe UI", "Helvetica Neue", sans-serif'
- ,line_height: "1.8"
- ,font_size: "16px"
- ,font_weight: "400"
- ,max_width: "820px"
- ,margin: "0 auto"
- };
-
- if( RT.config.theme && RT.config.theme.meta_is_dark === false ){
- RT.config.article.font_weight = "600";
- }
-
- const conf = RT.config.article;
- const article_seq = document.querySelectorAll("RT-article");
-
- if(article_seq.length === 0) return;
-
- for(let i = 0; i < article_seq.length; i++){
- let style = article_seq[i].style;
- style.display = "block";
- style.fontFamily = conf.font_family;
- style.fontSize = conf.font_size;
- style.lineHeight = conf.line_height;
- style.fontWeight = conf.font_weight;
- style.maxWidth = conf.max_width;
- style.margin = conf.margin;
- style.padding = "0 20px";
- style.color = "var(--rt-content-main)";
- }
-
- window.RT = window.RT || {};
- window.RT.config = window.RT.config || {};
- window.RT.config.page = window.RT.config.page || {};
- window.RT.config.page.height_limit = 900;
-
- const style_node = document.createElement("style");
- style_node.innerHTML = `
- body, html, rt-article {
- overflow-anchor: none !important;
- }
-
- rt-article {
- font-family: 'Noto Sans JP', Arial, sans-serif;
- background-color: var(--rt-surface-0);
- color: var(--rt-content-main);
- max-width: 46.875rem !important;
- box-sizing: border-box !important;
- }
-
- rt-article:not(:has(rt-page)) {
- padding: 3rem !important;
- }
-
- rt-article:has(rt-page) {
- padding: 0 !important;
- }
-
- rt-article rt-page {
- position: relative;
- display: block;
- padding: 3rem;
- margin: 1.25rem auto;
- background-color: var(--rt-surface-0);
- box-shadow: 0 0 0.625rem var(--rt-brand-primary);
- }
-
- rt-article h1 {
- font-size: 1.5rem;
- text-align: center;
- color: var(--rt-brand-primary);
- font-weight: 500;
- margin-top: 1.5rem;
- line-height: 1.15;
- }
- rt-article h2 {
- font-size: 1.25rem;
- color: var(--rt-brand-secondary);
- text-align: left;
- margin-top: 2rem;
- margin-left: 0;
- }
- rt-article h3 {
- font-size: 1.125rem;
- color: var(--rt-brand-tertiary);
- text-align: left;
- margin-top: 1.5rem;
- margin-left: 4ch;
- }
- rt-article h4 {
- font-size: 1.05rem;
- color: var(--rt-content-main);
- font-weight: 600;
- text-align: left;
- margin-top: 1.25rem;
- margin-left: 8ch;
- }
-
- rt-article p,
- rt-article ul,
- rt-article ol {
- color: var(--rt-content-main);
- text-align: justify;
- margin-bottom: 1rem;
- margin-left: 0;
- }
- rt-article li {
- margin-bottom: 0.5rem;
- }
-
- rt-article rt-code {
- font-family: 'Courier New', Courier, monospace;
- background-color: var(--rt-surface-code);
- padding: 0.125rem 0.25rem;
- color: var(--rt-content-main);
- }
-
- rt-article img {
- max-width: 100%;
- height: auto;
- display: block;
- margin: 1.5rem auto;
- }
- `;
- document.head.appendChild(style_node);
- };
-
- // 7. The Execution Sequence
- function run_semantics(){
- debug.log('scroll' ,`4. run_semantics starting.`);
- if(RT.theme) RT.theme();
- if(RT.endnote) RT.endnote();
- RT.article();
- if(RT.title) RT.title();
- if(RT.term) RT.term();
- if(RT.math) RT.math();
- if(RT.code) RT.code();
- if(RT.symbol) RT.symbol();
- if(RT.constraint) RT.constraint();
- if(RT.crossref) RT.crossref();
-
- if( window.MathJax && MathJax.Hub && MathJax.Hub.Queue ){
- MathJax.Hub.Queue( ["Typeset" ,MathJax.Hub] ,run_layout );
- }else{
- run_layout();
- }
- }
-
- function run_layout() {
- debug.log('scroll', `5. run_layout starting.`);
-
- if(RT.chapter) RT.chapter();
- if(RT.TOC) RT.TOC();
- if(RT.paginate_by_element) RT.paginate_by_element();
- if(RT.page) RT.page();
-
- debug.log('scroll', `6. Pagination complete.`);
-
- let final_target = target_y;
- let use_hash = false;
-
- if (window.location.hash && !is_reload) {
- const hash_target = document.getElementById(window.location.hash.substring(1));
- if (hash_target) {
- use_hash = true;
- }
- }
-
- debug.log('scroll', `7. Commencing viewport enforce loop. Mode: ${use_hash ? 'HASH' : 'Y-COORDINATE'}`);
- enforce_scroll(final_target, use_hash, 0);
- }
-
- // 8. The Enforcer Logic
- function enforce_scroll(target, use_hash, attempts) {
- if (attempts > 15) {
- debug.log('scroll', "8. Scroll enforcement timed out. Unlocking.");
- unlock_layout();
- return;
- }
-
- if (use_hash) {
- const hash_target = document.getElementById(window.location.hash.substring(1));
- if (hash_target) {
- hash_target.scrollIntoView();
- debug.log('scroll', `8a. Attempt ${attempts}: Scrolled to Hash Target. Y is now ${window.scrollY}`);
- }
- } else {
- window.scrollTo(0, target);
- debug.log('scroll', `8b. Attempt ${attempts}: Scrolled to Y=${target}. Current Y is ${window.scrollY}`);
- }
-
- let is_successful = false;
- if (use_hash) {
- is_successful = true;
- } else {
- is_successful = (Math.abs(window.scrollY - target) < 5 || target === 0);
- }
-
- if (is_successful && document.body.scrollHeight > 1000) {
- debug.log('scroll', `9. Viewport anchored successfully.`);
-
- setTimeout(() => {
- if (!use_hash && Math.abs(window.scrollY - target) >= 5) {
- debug.log('scroll', `9a. Browser late-stage rebellion detected. Re-enforcing.`);
- enforce_scroll(target, use_hash, attempts + 1);
- } else {
- unlock_layout();
- }
- }, 100);
- } else {
- setTimeout(() => enforce_scroll(target, use_hash, attempts + 1), 50);
- }
- }
-
- // 9. The Ledger
- let scroll_timer;
- window.addEventListener('scroll', () => {
- if (is_layout_locked) return;
- clearTimeout(scroll_timer);
- scroll_timer = setTimeout(() => {
- sessionStorage.setItem('RT_saved_y', window.scrollY);
- debug.log('scroll', `X. User stopped scrolling. Saved Y: ${window.scrollY}`);
- }, 200);
- }, { passive: true });
-
- window.addEventListener('beforeunload', () => {
- is_layout_locked = true;
- debug.log('scroll', "Y. Page unloading. Scroll listener locked.");
- });
-
- // 10. Bind to DOM Ready
- document.addEventListener('DOMContentLoaded', run_semantics);
-
-})();
-
+++ /dev/null
-/*
- <head>
- <script>
- window.RT.load('layout/memo_state_dept');
- </script>
- </head>
- <body>
- <RT-memo>
- </RT-memo>
- </body>
-*/
-
-(function(){
- const RT = window.RT = window.RT || {};
-
- // 1. Declare Dependencies
- RT.load('core/utility');
- RT.load('element/title');
- RT.load('element/term');
- RT.load('element/TOC');
- RT.load('core/body_visibility_visible');
-
- // 2. The Typography Layout
- RT.memo_state_dept = function(){
- const body = document.body;
- const html = document.documentElement;
-
- // Force strict print colors regardless of user system settings
- html.style.backgroundColor = "white";
- body.style.backgroundColor = "white";
- body.style.color = "black";
-
- // Target the new semantic tag
- const memo_seq = document.querySelectorAll("RT-memo");
- if(memo_seq.length === 0) return;
-
- for(let i = 0; i < memo_seq.length; i++){
- let style = memo_seq[i].style;
- style.display = "block";
- style.fontFamily = '"Times New Roman", Times, serif';
- style.fontSize = "12pt";
- style.lineHeight = "1.15";
- // 8.5 inch standard width minus 1-inch margins on each side
- style.maxWidth = "6.5in";
- style.margin = "1in auto";
- style.padding = "0";
- style.textAlign = "left";
- style.color = "black";
- }
- };
-
- // 3. The Execution Sequence
- const run_semantics = function(){
- RT.memo_state_dept();
-
- if(RT.title) RT.title();
- if(RT.term) RT.term();
- if(RT.TOC) RT.TOC();
-
- run_layout();
- };
-
- const run_layout = function(){
- if(RT.body_visibility_visible) RT.body_visibility_visible();
- };
-
- // 4. Bind to DOM Ready
- document.addEventListener('DOMContentLoaded' ,run_semantics);
-
-})();
+++ /dev/null
-/*
- Page Layout: Fixed Glow
- Standard: Theme 1.0
- Description: A variable-height container with a glowing border effect that matches the active theme.
-*/
-(function(){
- const RT = window.RT = window.RT || {};
-
- RT.page = function() {
- RT.config = RT.config || {};
-
- // Default Configuration
- const defaults = {
- width: "100%"
- ,min_height: "15rem" // Replaces fixed height
- ,padding: "3rem"
- ,margin: "4rem auto"
-
- ,bg_color: "var(--rt-surface-0)"
- ,border_color: "var(--rt-brand-primary)"
- ,text_color: "var(--rt-brand-primary)"
-
- ,shadow: "drop-shadow(0px 0px 15px var(--rt-brand-primary))"
- };
-
- RT.config.page = Object.assign({}, defaults, RT.config.page || {});
-
- const conf = RT.config.page;
- const style_id = 'rt-page-fixed-glow';
-
- if (!document.getElementById(style_id)) {
- const style_el = document.createElement('style');
- style_el.id = style_id;
-
- style_el.textContent = `
- /* Reset page counter on the article container */
- rt-article {
- counter-reset: rt-page-counter;
- }
-
- rt-page {
- display: block;
- position: relative;
- box-sizing: border-box;
- overflow: hidden;
-
- /* Dimensions */
- width: ${conf.width};
- min-height: ${conf.min_height};
- margin: ${conf.margin};
- padding: ${conf.padding};
-
- /* Theming */
- background-color: ${conf.bg_color};
- border: 1px solid ${conf.border_color};
-
- /* The "Glow" Effect */
- filter: ${conf.shadow};
-
- /* Counter Increment */
- counter-increment: rt-page-counter;
- }
-
- /* Page Numbering */
- rt-page::after {
- content: "Page " counter(rt-page-counter);
- position: absolute;
- bottom: 1.5rem;
- right: 3rem;
-
- font-family: "Noto Sans", sans-serif;
- font-size: 0.9rem;
- font-weight: bold;
-
- color: ${conf.text_color};
- opacity: 0.8;
- pointer-events: none;
- }
- `;
- document.head.appendChild(style_el);
- }
- };
-})();
+++ /dev/null
-window.RT.paginate_by_element = function () {
- const RT = window.RT;
- const debug = RT.debug || { log: function(){}, error: function(){} };
- const page_conf = (RT.config && RT.config.page) ? RT.config.page : {};
- const page_height_limit = page_conf.height_limit || 1000;
-
- let measureContainer = null;
-
- // =========================================================
- // 1. DOM Measurement Utilities
- // =========================================================
- function getElHeight(el) {
- const wasInDOM = el.parentNode !== null;
- if (!wasInDOM) document.body.appendChild(el);
- const rect = el.getBoundingClientRect();
- const style = window.getComputedStyle(el);
- const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom);
- if (!wasInDOM) el.remove();
- return (rect.height || 0) + (margin || 0);
- }
-
- function getMeasureContainer() {
- if (measureContainer && measureContainer.parentNode) return measureContainer;
- const article = document.querySelector('RT-article');
- if (!article) {
- const temp = document.createElement('div');
- temp.style.visibility = 'hidden';
- temp.style.position = 'absolute';
- temp.style.width = '100%';
- document.body.appendChild(temp);
- measureContainer = temp;
- return temp;
- }
- const container = document.createElement('div');
- const articleStyle = window.getComputedStyle(article);
- container.style.visibility = 'hidden';
- container.style.position = 'absolute';
- container.style.width = articleStyle.width;
- container.style.fontFamily = articleStyle.fontFamily;
- container.style.fontSize = articleStyle.fontSize;
- container.style.lineHeight = articleStyle.lineHeight;
- container.style.fontWeight = articleStyle.fontWeight;
- document.body.appendChild(container);
- measureContainer = container;
- return container;
- }
-
- function measureFragment(frag) {
- const container = getMeasureContainer();
- container.appendChild(frag);
- const h = getElHeight(frag);
- container.removeChild(frag);
- return h;
- }
-
- // =========================================================
- // STEP 1: PREPARE FOOTNOTES (Strip and tag)
- // =========================================================
- const article_seq = document.querySelectorAll('RT-article');
- if (article_seq.length === 0) {
- debug.error('pagination', 'No <RT-article> elements found. Pagination aborted.');
- return;
- }
-
- const footnote_registry = {};
- let footnote_counter = 1;
-
- Array.from(article_seq).forEach(article => {
- // Bulletproof extraction: immune to XML/HTML case-sensitivity parsing quirks
- const all_nodes = Array.from(article.querySelectorAll('*'));
- const raw_footnotes = all_nodes.filter(node => node.tagName.toLowerCase() === 'rt-footnote');
-
- raw_footnotes.forEach(fn => {
- const id = footnote_counter++;
- footnote_registry[id] = fn.innerHTML; // Save the payload
-
- // Trim any standard HTML whitespace immediately preceding the tag
- const prev = fn.previousSibling;
- if (prev && prev.nodeType === Node.TEXT_NODE) {
- prev.textContent = prev.textContent.replace(/\s+$/, '');
- }
-
- // Replace with a zero-height marker that rides along with the text
- const marker = document.createElement('rt-fn-marker');
- marker.setAttribute('data-id', id);
-
- if (fn.parentNode) {
- fn.parentNode.replaceChild(marker, fn);
- }
- });
- });
-
- // =========================================================
- // Splitting Logic (Clean and undisturbed)
- // =========================================================
- function isSplittable(el) {
- const tag = el.tagName;
- if (tag === 'UL' || tag === 'OL') {
- const items = Array.from(el.children).filter(c => c.tagName === 'LI');
- if (items.length === 0) return null;
-
- const itemHeights = items.map(li => getElHeight(li));
- const emptyClone = el.cloneNode(false);
- const overhead = getElHeight(emptyClone);
-
- el._splitInfo = { type: 'list', itemHeights, overhead, offset: 0 };
- return makeListSplitter(el, el._splitInfo);
- }
-
- if (tag === 'TABLE') {
- const thead = el.querySelector('thead');
- const tbody = el.querySelector('tbody');
- const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows);
- if (rows.length === 0) return null;
-
- const theadHeight = thead ? getElHeight(thead) : 0;
- const rowHeights = rows.map(row => getElHeight(row));
-
- const emptyClone = el.cloneNode(false);
- if (thead) emptyClone.appendChild(thead.cloneNode(true));
- emptyClone.appendChild(document.createElement('tbody'));
- const overhead = getElHeight(emptyClone) - theadHeight;
-
- el._splitInfo = { type: 'table', rowHeights, overhead, theadHeight, offset: 0 };
- return makeTableSplitter(el, el._splitInfo);
- }
- return null;
- }
-
- function makeListSplitter(el, info) {
- return (remaining) => {
- const children = Array.from(el.children).filter(c => c.tagName === 'LI');
- const start = info.offset;
-
- let bestCount = 0;
- let bestHeight = 0;
- const tempList = el.cloneNode(false);
-
- for (let i = 0; i < children.length; i++) {
- const itemClone = children[i].cloneNode(true);
- tempList.appendChild(itemClone);
- const fragHeight = measureFragment(tempList);
- if (fragHeight <= remaining) {
- bestCount = i + 1;
- bestHeight = fragHeight;
- } else {
- tempList.removeChild(itemClone);
- break;
- }
- }
-
- if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 };
-
- const first = el.cloneNode(false);
- for (let i = 0; i < bestCount; i++) {
- first.appendChild(children[i].cloneNode(true));
- }
-
- let rest = null;
- if (bestCount < children.length) {
- rest = el.cloneNode(false);
- for (let i = bestCount; i < children.length; i++) {
- rest.appendChild(children[i].cloneNode(true));
- }
-
- if (el.tagName === 'OL') {
- const currentStart = parseInt(el.getAttribute('start'), 10) || 1;
- rest.setAttribute('start', currentStart + bestCount);
- }
-
- rest._splitInfo = {
- type: 'list',
- itemHeights: info.itemHeights,
- overhead: info.overhead,
- offset: start + bestCount
- };
- }
-
- return { first, rest, firstHeight: bestHeight };
- };
- }
-
- function makeTableSplitter(el, info) {
- const thead = el.querySelector('thead');
- const createShell = () => {
- const shell = el.cloneNode(false);
- if (thead) shell.appendChild(thead.cloneNode(true));
- const newTbody = document.createElement('tbody');
- shell.appendChild(newTbody);
- return shell;
- };
-
- return (remaining) => {
- const tbody = el.querySelector('tbody');
- const rows = tbody ? Array.from(tbody.rows) : Array.from(el.rows);
- const start = info.offset;
-
- let bestCount = 0;
- let bestHeight = 0;
- const tempTable = createShell();
- const tempBody = tempTable.querySelector('tbody');
-
- for (let i = 0; i < rows.length; i++) {
- tempBody.appendChild(rows[i].cloneNode(true));
- const h = measureFragment(tempTable);
- if (h <= remaining) {
- bestCount = i + 1;
- bestHeight = h;
- } else {
- tempBody.removeChild(tempBody.lastChild);
- break;
- }
- }
-
- if (bestCount === 0) return { first: null, rest: el, firstHeight: 0 };
-
- const first = createShell();
- const firstBody = first.querySelector('tbody');
- for (let i = 0; i < bestCount; i++) {
- firstBody.appendChild(rows[i].cloneNode(true));
- }
-
- let rest = null;
- if (bestCount < rows.length) {
- rest = createShell();
- const restBody = rest.querySelector('tbody');
- for (let i = bestCount; i < rows.length; i++) {
- restBody.appendChild(rows[i].cloneNode(true));
- }
-
- rest._splitInfo = {
- type: 'table',
- rowHeights: info.rowHeights,
- overhead: info.overhead,
- theadHeight: info.theadHeight,
- offset: start + bestCount
- };
- }
-
- return { first, rest, firstHeight: bestHeight };
- };
- }
-
- // =========================================================
- // STEP 2: NORMAL PAGINATOR
- // =========================================================
- function paginateArticle(article) {
- const raw_element_seq = Array.from(article.children).filter(el =>
- !['SCRIPT', 'STYLE', 'RT-PAGE'].includes(el.tagName)
- );
-
- if (raw_element_seq.length === 0) return;
-
- const page_seq = [];
- let current_batch_seq = [];
- let current_h = 0;
- let i = 0;
-
- while (i < raw_element_seq.length) {
- const el = raw_element_seq[i];
- const splitter = isSplittable(el);
-
- if (splitter) {
- const remaining = page_height_limit - current_h;
- const { first, rest, firstHeight } = splitter(remaining);
-
- if (first) {
- current_batch_seq.push(first);
- current_h += firstHeight;
-
- if (rest) {
- raw_element_seq.splice(i, 1, rest);
- } else {
- raw_element_seq.splice(i, 1);
- }
- } else {
- if (current_batch_seq.length === 0) {
- const frame = document.createElement('rt-scroll-frame');
- frame.style.display = 'block';
- frame.style.overflowY = 'auto';
- frame.style.maxHeight = page_height_limit + 'px';
- frame.appendChild(el);
- current_batch_seq.push(frame);
- i++;
- } else {
- page_seq.push(current_batch_seq);
- current_batch_seq = [];
- current_h = 0;
- raw_element_seq[i] = rest || el;
- }
- }
- continue;
- }
-
-
- // --- Ordinary (non-splittable) element ---
- const h = getElHeight(el);
- const is_RT_page_break = el.tagName && el.tagName.toLowerCase() === 'rt-page-break';
-
- if( (is_RT_page_break || current_h + h > page_height_limit) && current_batch_seq.length > 0 ){
- let backtrack_seq = [];
- let backtrack_h = 0;
-
- while (current_batch_seq.length > 0) {
- const last = current_batch_seq[current_batch_seq.length - 1];
- if (!/^H[1-6]/.test(last.tagName)) break;
- const popped = current_batch_seq.pop();
- backtrack_seq.unshift(popped);
- backtrack_h += getElHeight(popped);
- }
-
- if (current_batch_seq.length > 0) {
- page_seq.push(current_batch_seq);
- current_batch_seq = backtrack_seq;
- current_h = backtrack_h;
- } else {
- page_seq.push(backtrack_seq);
- current_batch_seq = [];
- current_h = 0;
- }
- }
-
- current_batch_seq.push(el);
- current_h += h;
- i++;
- }
-
- if (current_batch_seq.length > 0) {
- page_seq.push(current_batch_seq);
- }
-
- // Rebuild article with <rt-page> wrappers
- article.innerHTML = '';
- let p = 0;
- while (p < page_seq.length) {
- const batch = page_seq[p];
- const page_el = document.createElement('rt-page');
- page_el.id = `page-${p + 1}`;
- batch.forEach(item => page_el.appendChild(item));
- article.appendChild(page_el);
- p++;
- }
- }
-
- // Execute pagination
- Array.from(article_seq).forEach(article => paginateArticle(article));
-
- // =========================================================
- // STEP 3: RESOLVE FOOTNOTES & EXPAND PAGES
- // =========================================================
- Array.from(article_seq).forEach(article => {
- const rendered_pages = article.querySelectorAll('rt-page');
-
- Array.from(rendered_pages).forEach(page => {
- // Bulletproof extraction for the markers
- const all_page_nodes = Array.from(page.querySelectorAll('*'));
- const markers = all_page_nodes.filter(node => node.tagName.toLowerCase() === 'rt-fn-marker');
-
- if (markers.length === 0) return;
-
- // Construct the footer block for this page
- const fn_container = document.createElement('div');
- fn_container.className = 'rt-footnote-container';
- fn_container.style.borderTop = '1px solid var(--rt-border-default)';
- fn_container.style.marginTop = '2rem';
- fn_container.style.paddingTop = '1rem';
- fn_container.style.fontSize = '0.9em';
-
- markers.forEach(marker => {
- const id = marker.getAttribute('data-id');
- const html = footnote_registry[id];
-
- // Replace the invisible marker with the visible naked superscript link
- const sup = document.createElement('sup');
- sup.innerHTML = `<a href="#fn-${id}" id="fn-ref-${id}" style="color: var(--rt-brand-link); text-decoration: none;">${id}</a>`;
-
- if (marker.parentNode) {
- marker.parentNode.replaceChild(sup, marker);
- }
-
- // Append the actual text to the footer with a clean, print-ready number format
- const fn_line = document.createElement('div');
- fn_line.id = `fn-${id}`;
- fn_line.style.marginBottom = '0.5rem';
- fn_line.innerHTML = `<span style="padding-right: 0.5em; font-weight: 600;">${id}.</span>${html}`;
- fn_container.appendChild(fn_line);
- });
-
- // Attach the footer. The page organically stretches to fit.
- page.appendChild(fn_container);
- });
- });
-
- // Cleanup
- if (measureContainer && measureContainer.parentNode) {
- measureContainer.remove();
- measureContainer = null;
- }
-};
+++ /dev/null
-/*
- Theme: Inverse Wheat (Dark)
- Standard: Theme 1.0
- Description: High contrast Amber on Deep Charcoal.
-*/
-( function(){
- const RT = window.RT = window.RT || {};
-
- RT.theme = function(){
- RT.config = RT.config || {};
-
- // THEME 1.0 DATA CONTRACT
- RT.config.theme = {
- meta_is_dark: true
- ,meta_name: "Inverse Wheat"
-
- // --- SURFACES (Depth & Container Hierarchy) ---
- ,surface_0: "hsl(0, 0%, 5%)" // App Background (Deepest)
- ,surface_1: "hsl(0, 0%, 10%)" // Sidebar / Nav / Panels
- ,surface_2: "hsl(0, 0%, 14%)" // Cards / Floating Elements
- ,surface_3: "hsl(0, 0%, 18%)" // Modals / Dropdowns / Popovers
- ,surface_input: "hsl(0, 0%, 12%)" // Form Inputs
- ,surface_code: "hsl(0, 0%, 11%)" // Code Block Background
- ,surface_select: "hsl(45, 100%, 15%)" // Text Selection Highlight
-
- // --- CONTENT (Text & Icons) ---
- ,content_main: "hsl(50, 60%, 85%)" // Primary Reading Text
- ,content_muted: "hsl(36, 15%, 60%)" // Metadata, subtitles
- ,content_subtle: "hsl(36, 10%, 40%)" // Placeholders, disabled states
- ,content_inverse: "hsl(0, 0%, 5%)" // Text on high-contrast buttons
-
- // --- BRAND & ACTION (The "Wheat" Identity) ---
- ,brand_primary: "hsl(45, 100%, 50%)" // Main Action / H1 / Focus Ring
- ,brand_secondary: "hsl(38, 90%, 65%)" // Secondary Buttons / H2
- ,brand_tertiary: "hsl(30, 60%, 70%)" // Accents / H3
- ,brand_link: "hsl(48, 100%, 50%)" // Hyperlinks (High Visibility)
-
- // --- BORDERS & DIVIDERS ---
- ,border_faint: "hsl(36, 20%, 15%)" // Subtle separation
- ,border_default: "hsl(36, 20%, 25%)" // Standard Card Borders
- ,border_strong: "hsl(36, 20%, 40%)" // Active states / Inputs
-
- // --- STATE & FEEDBACK (Earth Tones) ---
- ,state_success: "hsl(100, 50%, 45%)" // Olive Green
- ,state_warning: "hsl(35, 90%, 55%)" // Burnt Orange
- ,state_error: "hsl(0, 60%, 55%)" // Brick Red
- ,state_info: "hsl(200, 40%, 55%)" // Slate Blue
-
- // --- SYNTAX HIGHLIGHTING (For Code) ---
- ,syntax_keyword: "hsl(35, 100%, 65%)" // Orange
- ,syntax_string: "hsl(75, 50%, 60%)" // Sage Green
- ,syntax_func: "hsl(45, 90%, 70%)" // Light Gold
- ,syntax_comment: "hsl(36, 15%, 45%)" // Brown/Gray
- };
-
- // --- APPLY THEME ---
- const palette = RT.config.theme;
- const body = document.body;
- const html = document.documentElement;
-
- // 1. Paint Base
- html.style.backgroundColor = palette.surface_0;
- body.style.backgroundColor = palette.surface_0;
- body.style.color = palette.content_main;
-
- // 2. Export Variables (Standardization)
- const s = body.style;
- for (const [key, value] of Object.entries(palette)) {
- s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
- }
-
-
- // 3. Global Overrides
- const style_id = 'rt-global-overrides';
- if (!document.getElementById(style_id)) {
- const style = document.createElement('style');
- style.id = style_id;
- style.textContent = `
- ::selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
- ::-moz-selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
-
- ::-webkit-scrollbar { width: 12px; }
- ::-webkit-scrollbar-track { background: var(--rt-surface-0); }
- ::-webkit-scrollbar-thumb {
- background: var(--rt-border-default);
- border: 2px solid var(--rt-surface-0);
- border-radius: 8px;
- }
- ::-webkit-scrollbar-thumb:hover { background: var(--rt-brand-secondary); }
-
- /* --- Citation & Endnote Styling --- */
- rt-cite a, .rt-inline-cite a, rt-endnotes a {
- color: var(--rt-brand-link);
- text-decoration: none;
- }
- rt-cite a:hover, .rt-inline-cite a:hover, rt-endnotes a:hover {
- text-decoration: underline;
- }
- rt-cite, .rt-inline-cite {
- font-size: 1em;
- vertical-align: baseline;
- padding: 0 0.15em;
- }
-
- /* --- Image Inversion for Diagrams --- */
- img.rt-diagram {
- filter: invert(1) hue-rotate(180deg);
- }
- `;
-
- document.head.appendChild(style);
- }
- };
-
-} )();
+++ /dev/null
-/*
- Theme: Classic Wheat (Light)
- Standard: Theme 1.0
- Description: Warm paper tones with Burnt Orange accents.
-*/
-( function(){
- const RT = window.RT = window.RT || {};
-
- RT.theme_light = function(){
- RT.config = RT.config || {};
-
- // THEME 1.0 DATA CONTRACT
- RT.config.theme = {
- meta_is_dark: false
- ,meta_name: "Classic Wheat"
-
- // --- SURFACES ---
- ,surface_0: "hsl(40, 30%, 94%)" // App Background (Cream/Linen)
- ,surface_1: "hsl(40, 25%, 90%)" // Sidebar (Slightly darker beige)
- ,surface_2: "hsl(40, 20%, 98%)" // Cards (Lighter, almost white)
- ,surface_3: "hsl(0, 0%, 100%)" // Modals (Pure White)
- ,surface_input: "hsl(40, 20%, 98%)" // Form Inputs
- ,surface_code: "hsl(40, 15%, 90%)" // Code Block Background
- ,surface_select: "hsl(45, 100%, 85%)" // Text Selection Highlight
-
- // --- CONTENT ---
- ,content_main: "hsl(30, 20%, 20%)" // Deep Umber (Not Black)
- ,content_muted: "hsl(30, 15%, 45%)" // Medium Brown
- ,content_subtle: "hsl(30, 10%, 65%)" // Light Brown/Gray
- ,content_inverse: "hsl(40, 30%, 94%)" // Text on dark buttons
-
- // --- BRAND & ACTION ---
- ,brand_primary: "hsl(30, 90%, 35%)" // Burnt Orange (Action)
- ,brand_secondary: "hsl(35, 70%, 45%)" // Rust / Gold
- ,brand_tertiary: "hsl(25, 60%, 55%)" // Copper
- ,brand_link: "hsl(30, 100%, 35%)" // Link Color
-
- // --- BORDERS ---
- ,border_faint: "hsl(35, 20%, 85%)"
- ,border_default: "hsl(35, 20%, 75%)"
- ,border_strong: "hsl(35, 20%, 55%)"
-
- // --- STATE & FEEDBACK ---
- ,state_success: "hsl(100, 40%, 40%)" // Forest Green
- ,state_warning: "hsl(30, 90%, 50%)" // Persimmon
- ,state_error: "hsl(0, 60%, 45%)" // Crimson
- ,state_info: "hsl(200, 50%, 45%)" // Navy Blue
-
- // --- SYNTAX ---
- ,syntax_keyword: "hsl(20, 90%, 45%)" // Rust
- ,syntax_string: "hsl(100, 35%, 35%)" // Ivy Green
- ,syntax_func: "hsl(300, 30%, 40%)" // Muted Purple
- ,syntax_comment: "hsl(35, 10%, 60%)" // Light Brown
- };
-
- // --- APPLY THEME ---
- const palette = RT.config.theme;
- const body = document.body;
- const html = document.documentElement;
-
- html.style.backgroundColor = palette.surface_0;
- body.style.backgroundColor = palette.surface_0;
- body.style.color = palette.content_main;
-
- const s = body.style;
- for (const [key, value] of Object.entries(palette)) {
- s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
- }
- };
-} )();
+++ /dev/null
-/*
- Theme: Golden Wheat (Light) - "Spanish Gold Edition"
- File: style/theme-light-gold.js
- Standard: Theme 1.0
- Description: Light Parchment background with Oxblood Red ink.
-*/
-( function(){
- const RT = window.RT = window.RT || {};
-
- RT.theme = function(){
- RT.config = RT.config || {};
-
- RT.config.theme = {
- meta_is_dark: false
- ,meta_name: "Golden Wheat (Yellow)"
-
- // --- SURFACES (Light Parchment) ---
- // Shifted lightness up to 94% for a "whiter" feel that still holds the yellow tint.
- ,surface_0: "hsl(48, 50%, 94%)" // Main Page: Fine Parchment
- ,surface_1: "hsl(48, 40%, 90%)" // Panels: Slightly darker
- ,surface_2: "hsl(48, 30%, 97%)" // Cards: Very light
- ,surface_3: "hsl(0, 0%, 100%)" // Popups
- ,surface_input: "hsl(48, 20%, 96%)"
- ,surface_code: "hsl(48, 25%, 88%)" // Distinct Code BG
- ,surface_select: "hsl(10, 70%, 85%)" // Red Highlight
-
- // --- CONTENT (Deep Ink) ---
- ,content_main: "hsl(10, 25%, 7%)" // Deep Warm Black (Ink)
- ,content_muted: "hsl(10, 15%, 35%)" // Dark Grey-Red
- ,content_subtle: "hsl(10, 10%, 55%)"
- ,content_inverse: "hsl(48, 50%, 90%)"
-
- // --- BRAND & ACTION (The Red Spectrum) ---
- ,brand_primary: "hsl(12, 85%, 30%)" // H1 (Deep Oxblood)
- ,brand_secondary: "hsl(10, 80%, 35%)" // H2 (Garnet)
- ,brand_tertiary: "hsl(8, 70%, 40%)" // H3 (Brick)
- ,brand_link: "hsl(12, 90%, 35%)" // Link
-
- // --- BORDERS ---
- ,border_faint: "hsl(45, 30%, 80%)"
- ,border_default: "hsl(45, 30%, 70%)" // Pencil Grey
- ,border_strong: "hsl(12, 50%, 40%)"
-
- // --- STATE ---
- ,state_success: "hsl(120, 40%, 30%)"
- ,state_warning: "hsl(25, 90%, 45%)"
- ,state_error: "hsl(0, 75%, 35%)"
- ,state_info: "hsl(210, 60%, 40%)"
-
- // --- SYNTAX ---
- ,syntax_keyword: "hsl(0, 75%, 35%)"
- ,syntax_string: "hsl(100, 35%, 25%)"
- ,syntax_func: "hsl(15, 85%, 35%)"
- ,syntax_comment: "hsl(45, 20%, 50%)"
- };
-
- // --- APPLY THEME ---
- const palette = RT.config.theme;
- const body = document.body;
- const html = document.documentElement;
-
- html.style.backgroundColor = palette.surface_0;
- body.style.backgroundColor = palette.surface_0;
- body.style.color = palette.content_main;
-
- const s = body.style;
- for (const [key, value] of Object.entries(palette)) {
- s.setProperty(`--rt-${key.replace(/_/g, '-')}`, value);
- }
-
- // Global overrides
- const style_id = 'rt-global-overrides';
- if (!document.getElementById(style_id)) {
- const style = document.createElement('style');
- style.id = style_id;
- style.textContent = `
- ::selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
- ::-moz-selection { background: var(--rt-surface-select); color: var(--rt-brand-primary); }
-
- ::-webkit-scrollbar { width: 12px; }
- ::-webkit-scrollbar-track { background: var(--rt-surface-0); }
- ::-webkit-scrollbar-thumb {
- background: var(--rt-border-default);
- border: 2px solid var(--rt-surface-0);
- border-radius: 8px;
- }
- ::-webkit-scrollbar-thumb:hover { background: var(--rt-brand-secondary); }
-
- rt-article p, rt-article li {
- text-shadow: 0px 0px 0.5px rgba(0,0,0, 0.2);
- }
-
- .MathJax, .MathJax_Display, .mjx-chtml {
- color: var(--rt-content-main) !important;
- fill: var(--rt-content-main) !important;
- stroke: var(--rt-content-main) !important;
- }
- `;
- document.head.appendChild(style);
- }
- };
-
-} )();
+++ /dev/null
-import os
-import glob
-import base64
-import re
-from weasyprint import HTML
-
-# Find the image file
-img_path = None
-for search_dir in ['/mnt/data', '/tmp', '.']:
- for root, dirs, files in os.walk(search_dir):
- if 'money_circle.jpeg' in files:
- img_path = os.path.join(root, 'money_circle.jpeg')
- break
- if img_path:
- break
-
-img_b64 = ""
-if img_path:
- with open(img_path, "rb") as f:
- img_b64 = base64.b64encode(f.read()).decode('utf-8')
-
-html_content = """
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <meta charset="UTF-8">
- <title>On Cybersecurity and Commonsense</title>
- </head>
- <body>
- </body>
-</html>
-"""
-
-# String replacements (hyphens, 'may', 'just')
-html_content = html_content.replace('—', '-')
-html_content = re.sub(r'\bmay\b', 'can', html_content)
-html_content = re.sub(r'\bMay\b', 'Can', html_content)
-html_content = re.sub(r'\bjust\b', 'merely', html_content)
-html_content = re.sub(r'\bJust\b', 'Merely', html_content)
-
-# Title block replacement
-title_match = re.search(r'<rt-title\s+title="(.*?)"\s+author="(.*?)"\s+date="(.*?)"\s+copyright="(.*?)"\s*>\s*</rt-title>', html_content, re.IGNORECASE | re.DOTALL)
-if title_match:
- title, author, date, copyright_text = title_match.groups()
- if not copyright_text.startswith('©') and not copyright_text.startswith('©'):
- copyright_text = f"© {copyright_text}"
-
- title_html = f'''
- <div class="title-block">
- <h1>{title}</h1>
- <p class="meta-author">{author}</p>
- <p class="meta-date">{date}</p>
- <p class="meta-copyright">{copyright_text}</p>
- </div>
- '''
- html_content = html_content[:title_match.start()] + title_html + html_content[title_match.end():]
-
-# TOC replacement
-headings = re.findall(r'<h([12])\s*(id="(.*?)")?>(.*?)</h\1>', html_content, re.IGNORECASE)
-toc_html = '<div class="toc-block"><h2>Table of Contents</h2><ul>'
-for idx, (level, _, id_val, text) in enumerate(headings):
- if not id_val:
- id_val = f"heading-{idx}"
- html_content = re.sub(f'<h{level}>{text}</h{level}>', f'<h{level} id="{id_val}">{text}</h{level}>', html_content, count=1)
- toc_html += f'<li class="toc-h{level}"><a href="#{id_val}">{text}</a></li>'
-toc_html += '</ul></div>'
-
-html_content = re.sub(r'<RT-TOC[^>]*></RT-TOC>', toc_html, html_content, flags=re.IGNORECASE)
-
-# Image replacement
-if img_b64:
- html_content = re.sub(r'src="money_circle\.jpeg"', f'src="data:image/jpeg;base64,{img_b64}" class="content-image"', html_content)
-
-# Custom tags
-html_content = re.sub(r'<RT-article>', '<div class="rt-article">', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'</RT-article>', '</div>', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'<RT-term>', '<span class="rt-term">', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'</RT-term>', '</span>', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'<RT-term-em>', '<span class="rt-term-em">', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'</RT-term-em>', '</span>', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'<RT-neologism>', '<span class="rt-neologism">', html_content, flags=re.IGNORECASE)
-html_content = re.sub(r'</RT-neologism>', '</span>', html_content, flags=re.IGNORECASE)
-
-def replace_rt_math(m):
- content = m.group(1)
- if '\n' in content:
- return f'<div class="math-block math">{content}</div>'
- return f'<span class="math-inline math">{content}</span>'
-
-def replace_rt_code(m):
- content = m.group(1)
- if '\n' in content:
- return f'<pre class="rt-code-block">{content}</pre>'
- return f'<code class="rt-code-inline">{content}</code>'
-
-html_content = re.sub(r'<RT-math>(.*?)</RT-math>', replace_rt_math, html_content, flags=re.IGNORECASE | re.DOTALL)
-html_content = re.sub(r'<RT-code>(.*?)</RT-code>', replace_rt_code, html_content, flags=re.IGNORECASE | re.DOTALL)
-
-css_styles = """
- @page {
- size: letter;
- margin: 25mm 20mm;
- background-color: #FAFAFA;
- @bottom-center {
- content: counter(page);
- font-family: 'Georgia', serif;
- font-size: 10pt;
- color: #555;
- }
- }
- body {
- margin: 0;
- padding: 0;
- font-family: 'Georgia', serif;
- font-size: 11pt;
- line-height: 1.6;
- color: #2c3e50;
- background-color: #FAFAFA;
- }
- *, *::before, *::after { box-sizing: border-box; }
-
- h1, h2, h3, h4 {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
- color: #1A365D;
- page-break-after: avoid;
- }
- h1 { font-size: 20pt; margin-top: 1.5em; margin-bottom: 0.5em; border-bottom: 2px solid #E2E8F0; padding-bottom: 0.2em; }
- h2 { font-size: 16pt; margin-top: 1.5em; margin-bottom: 0.5em; }
- p { margin-bottom: 1em; text-align: justify; }
- ul, ol { margin-bottom: 1em; padding-left: 2em; }
- li { margin-bottom: 0.5em; }
-
- .title-block { text-align: center; margin-bottom: 4em; padding: 2em 0; border-bottom: 3px solid #1A365D; }
- .title-block h1 { font-size: 26pt; border: none; margin-top: 0; margin-bottom: 0.5em; color: #0F2040; }
- .title-block .meta-author { font-size: 14pt; font-weight: bold; margin: 0.2em 0; }
- .title-block .meta-date { font-size: 12pt; color: #555; font-style: italic; margin: 0.2em 0; }
- .title-block .meta-copyright { font-size: 10pt; color: #777; margin-top: 1em; }
-
- .toc-block { background-color: #F0F4F8; padding: 20px; border-radius: 5px; margin-bottom: 3em; page-break-after: always; }
- .toc-block h2 { margin-top: 0; border: none; }
- .toc-block ul { list-style-type: none; padding-left: 0; }
- .toc-h1 { font-weight: bold; margin-top: 0.8em; }
- .toc-h2 { padding-left: 1.5em; font-size: 0.95em; }
- .toc-block a { text-decoration: none; color: #2c3e50; }
-
- .rt-term { font-weight: bold; color: #2980B9; }
- .rt-term-em { font-weight: bold; font-style: italic; color: #2980B9; }
- .rt-neologism { font-variant: small-caps; font-weight: bold; color: #C0392B; }
- .rt-code-inline { font-family: 'Courier New', Courier, monospace; background-color: #EAECEE; padding: 2px 4px; border-radius: 3px; font-size: 0.9em; }
- .rt-code-block { font-family: 'Courier New', Courier, monospace; background-color: #F4F6F7; padding: 15px; border-left: 4px solid #7F8C8D; border-radius: 3px; font-size: 0.9em; overflow-x: auto; white-space: pre-wrap; }
-
- .math { font-family: 'Times New Roman', serif; font-style: italic; font-weight: bold; color: #2C3E50; }
- .math-block { text-align: center; margin: 1.5em 0; font-size: 1.2em; background-color: #F9EBEA; padding: 10px; border-radius: 5px; }
- .math-inline { font-size: 1.05em; }
-
- table { width: 100%; border-collapse: collapse; margin: 2em 0; }
- th, td { padding: 10px; text-align: left; vertical-align: top; }
- th { background-color: #1A365D; color: white; font-weight: bold; }
- tr { border-bottom: 1px solid #ddd; }
- hr { border: 0; border-top: 1px solid #eee; }
-
- .content-image { display: block; max-width: 90%; margin: 2em auto; border: 1px solid #ccc; box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
-"""
-
-final_html = f"<!DOCTYPE html><html><head><meta charset='UTF-8'><style>{css_styles}</style></head><body>{html_content}</body></html>"
-HTML(string=final_html).write_pdf("White_Paper_Cybersecurity_Legislation.pdf")
-print("Success")
+++ /dev/null
-import re
-import os
-import glob
-from weasyprint import HTML
-
-def process_rt_html(html_str, out_file):
- # Fix escaped dollar signs
- html_str = html_str.replace('\\$', '$')
-
- # Remove hardcoded page breaks that cause blank pages
- html_str = re.sub(r'<div\s+style=["\']page-break-[^>]*["\']>\s*</div>', '', html_str, flags=re.IGNORECASE)
-
- css = """
- <style>
- @page {
- size: A4;
- margin: 25mm 20mm;
- background-color: #faf9f6;
- }
- *, *::before, *::after { box-sizing: border-box; }
- body {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
- font-size: 11pt;
- color: #2c3e50;
- line-height: 1.6;
- margin: 0;
- padding: 0;
- background-color: #faf9f6;
- }
- h1, h2, h3 {
- color: #1a252f;
- page-break-after: avoid;
- font-family: 'Georgia', serif;
- }
- h1 {
- font-size: 16pt;
- border-bottom: 2px solid #d4af37;
- padding-bottom: 5px;
- margin-top: 2em;
- }
- h2 {
- font-size: 14pt;
- margin-top: 1.5em;
- color: #2c3e50;
- }
- h3 {
- font-size: 12pt;
- margin-top: 1.2em;
- color: #34495e;
- }
- .rt-title-block {
- text-align: center;
- margin: -25mm -20mm 30px -20mm;
- padding: 35px 20mm;
- background-color: #1a252f;
- color: #ecf0f1;
- }
- .rt-title-block h1 {
- color: #d4af37;
- border: none;
- font-size: 20pt;
- margin: 0 0 15px 0;
- padding: 0;
- }
- .rt-meta {
- font-family: 'Georgia', serif;
- font-size: 11pt;
- color: #bdc3c7;
- font-style: italic;
- }
- .rt-copyright {
- font-size: 9pt;
- color: #95a5a6;
- margin-top: 10px;
- }
- .rt-term {
- border-bottom: 1px dashed #7f8c8d;
- font-style: italic;
- color: #2980b9;
- }
- .rt-term-plain {
- font-style: normal;
- }
- .rt-neologism {
- font-weight: bold;
- color: #c0392b;
- }
- .rt-neologism-plain {
- font-weight: normal;
- }
- .toc {
- background-color: #ffffff;
- border-left: 4px solid #d4af37;
- padding: 20px;
- margin: 25px 0;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
- /* Ensure the TOC breaks naturally, without forcing an empty page */
- }
- .toc-title {
- font-family: 'Georgia', serif;
- font-size: 14pt;
- font-weight: bold;
- color: #1a252f;
- margin-bottom: 15px;
- }
- .toc ul {
- list-style: none;
- padding-left: 0;
- margin: 0;
- }
- .toc li {
- margin-bottom: 8px;
- }
- .toc-h1 { font-weight: bold; margin-top: 12px; }
- .toc-h2 { margin-left: 20px; font-size: 10pt; color: #34495e; }
- .toc-h3 { margin-left: 40px; font-size: 10pt; color: #7f8c8d; }
- table {
- width: 100%;
- border-collapse: collapse;
- margin: 25px 0;
- background-color: #ffffff;
- }
- th, td {
- border: 1px solid #ecf0f1;
- padding: 12px;
- text-align: left;
- }
- th {
- background-color: #f4f6f7;
- color: #1a252f;
- font-weight: bold;
- }
- tr:nth-child(even) {
- background-color: #fafbfc;
- }
- ul, ol {
- margin: 15px 0;
- padding-left: 25px;
- }
- li {
- margin-bottom: 10px;
- }
- /* Let's remove this completely for this specific case to see if it lets the table and TOC share a page */
- /* .content-start {
- page-break-before: always;
- } */
- </style>
- """
-
- # Inject CSS
- html_str = html_str.replace("</head>", css + "</head>")
-
- # Process Title Block
- title_match = re.search(r'<RT-title\s+([^>]+)>(.*?)</RT-title>', html_str, re.DOTALL | re.IGNORECASE)
- if title_match:
- attrs = title_match.group(1)
- title = re.search(r'title="(.*?)"', attrs).group(1) if 'title="' in attrs else 'Document'
- author = re.search(r'author="(.*?)"', attrs).group(1) if 'author="' in attrs else ''
- date = re.search(r'date="(.*?)"', attrs).group(1) if 'date="' in attrs else ''
- copyright_txt = re.search(r'copyright="(.*?)"', attrs).group(1) if 'copyright="' in attrs else ''
-
- title_block = f'''
- <div class="rt-title-block">
- <h1>{title}</h1>
- <div class="rt-meta"><span class="author">{author}</span> | <span class="date">{date}</span></div>
- <div class="rt-copyright">{copyright_txt}</div>
- </div>
- '''
- html_str = html_str[:title_match.start()] + title_block + html_str[title_match.end():]
-
- # Process TOC
- toc_match = re.search(r'<RT-TOC\s+level="(.*?)"></RT-TOC>', html_str, re.IGNORECASE)
- if toc_match:
- level = toc_match.group(1)
- headings = []
- if '-' in level:
- headings = re.findall(r'<h([123])>(.*?)</h[123]>', html_str, re.IGNORECASE)
- else:
- headings = re.findall(rf'<h({level})>(.*?)</h{level}>', html_str, re.IGNORECASE)
-
- toc_html = '<div class="toc"><div class="toc-title">Table of Contents</div><ul>'
- for lvl, text in headings:
- # exclude COVER SHEET from TOC
- if "COVER SHEET" in text:
- continue
- toc_html += f'<li class="toc-h{lvl}">{text}</li>'
- toc_html += '</ul></div>'
-
- html_str = html_str[:toc_match.start()] + toc_html + html_str[toc_match.end():]
-
- # Term Replacement (First occurrence)
- seen_terms = set()
- def term_replace(match):
- text = match.group(1)
- l_text = text.lower()
- if l_text not in seen_terms:
- seen_terms.add(l_text)
- return f'<span class="rt-term">{text}</span>'
- else:
- return f'<span class="rt-term-plain">{text}</span>'
-
- html_str = re.sub(r'<RT-term>(.*?)</RT-term>', term_replace, html_str)
-
- # Neologism Replacement (First occurrence)
- seen_neos = set()
- def neo_replace(match):
- text = match.group(1)
- l_text = text.lower()
- if l_text not in seen_neos:
- seen_neos.add(l_text)
- return f'<span class="rt-neologism">{text}</span>'
- else:
- return f'<span class="rt-neologism-plain">{text}</span>'
-
- html_str = re.sub(r'<RT-neologism>(.*?)</RT-neologism>', neo_replace, html_str)
-
- HTML(string=html_str).write_pdf(out_file)
-
-# Locate the uploaded file
-uploaded_files = glob.glob('/mnt/data/user_uploads/*abstract.html')
-if not uploaded_files:
- uploaded_files = glob.glob('/mnt/data/uploads/*abstract.html')
-if not uploaded_files:
- # Look for it anywhere under /mnt/data
- for root, dirs, files in os.walk('/mnt/data'):
- for file in files:
- if file == 'abstract.html':
- uploaded_files.append(os.path.join(root, file))
-
-if uploaded_files:
- filepath = uploaded_files[0]
- with open(filepath, 'r', encoding='utf-8') as f:
- html_content = f.read()
- process_rt_html(html_content, 'abstract.pdf')
- print("PDF successfully generated.")
-else:
- print("Could not find abstract.html in the data directory.")
-
-Code output
-Traceback (most recent call last):
- File "<xbox-string>", line 1, in <module>
- import bs4
-ModuleNotFoundError: No module named 'bs4'
-
-Python
-import re
-import os
-import glob
-from weasyprint import HTML
-
-def process_rt_html(html_str, out_file):
- html_str = html_str.replace('\\$', '$')
- html_str = re.sub(r'<div\s+style=["\']page-break-[^>]*["\']>\s*</div>', '', html_str, flags=re.IGNORECASE)
-
- css = """
- <style>
- @page {
- size: A4;
- margin: 25mm 20mm;
- background-color: #faf9f6;
- }
- *, *::before, *::after { box-sizing: border-box; }
- body {
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
- font-size: 11pt;
- color: #2c3e50;
- line-height: 1.6;
- margin: 0;
- padding: 0;
- background-color: #faf9f6;
- }
- h1, h2, h3 {
- color: #1a252f;
- page-break-after: avoid;
- font-family: 'Georgia', serif;
- }
- h1 {
- font-size: 16pt;
- border-bottom: 2px solid #d4af37;
- padding-bottom: 5px;
- margin-top: 2em;
- }
- h2 {
- font-size: 14pt;
- margin-top: 1.5em;
- color: #2c3e50;
- }
- h3 {
- font-size: 12pt;
- margin-top: 1.2em;
- color: #34495e;
- }
- .rt-title-block {
- text-align: center;
- margin: -25mm -20mm 30px -20mm;
- padding: 35px 20mm;
- background-color: #1a252f;
- color: #ecf0f1;
- }
- .rt-title-block h1 {
- color: #d4af37;
- border: none;
- font-size: 20pt;
- margin: 0 0 15px 0;
- padding: 0;
- }
- .rt-meta {
- font-family: 'Georgia', serif;
- font-size: 11pt;
- color: #bdc3c7;
- font-style: italic;
- }
- .rt-copyright {
- font-size: 9pt;
- color: #95a5a6;
- margin-top: 10px;
- }
- .rt-term {
- border-bottom: 1px dashed #7f8c8d;
- font-style: italic;
- color: #2980b9;
- }
- .rt-term-plain {
- font-style: normal;
- }
- .rt-neologism {
- font-weight: bold;
- color: #c0392b;
- }
- .rt-neologism-plain {
- font-weight: normal;
- }
- .toc {
- background-color: #ffffff;
- border-left: 4px solid #d4af37;
- padding: 20px;
- margin: 25px 0;
- box-shadow: 0 2px 4px rgba(0,0,0,0.05);
- /* Ensure no forced break before TOC */
- page-break-before: auto;
- }
- .toc-title {
- font-family: 'Georgia', serif;
- font-size: 14pt;
- font-weight: bold;
- color: #1a252f;
- margin-bottom: 15px;
- }
- .toc ul {
- list-style: none;
- padding-left: 0;
- margin: 0;
- }
- .toc li {
- margin-bottom: 8px;
- }
- .toc-h1 { font-weight: bold; margin-top: 12px; }
- .toc-h2 { margin-left: 20px; font-size: 10pt; color: #34495e; }
- .toc-h3 { margin-left: 40px; font-size: 10pt; color: #7f8c8d; }
- table {
- width: 100%;
- border-collapse: collapse;
- margin: 25px 0;
- background-color: #ffffff;
- }
- th, td {
- border: 1px solid #ecf0f1;
- padding: 12px;
- text-align: left;
- }
- th {
- background-color: #f4f6f7;
- color: #1a252f;
- font-weight: bold;
- }
- tr:nth-child(even) {
- background-color: #fafbfc;
- }
- ul, ol {
- margin: 15px 0;
- padding-left: 25px;
- }
- li {
- margin-bottom: 10px;
- }
- .content-start {
- page-break-before: always;
- }
- </style>
- """
-
- html_str = html_str.replace("</head>", css + "</head>")
-
- title_match = re.search(r'<RT-title\s+([^>]+)>(.*?)</RT-title>', html_str, re.DOTALL | re.IGNORECASE)
- if title_match:
- attrs = title_match.group(1)
- title = re.search(r'title="(.*?)"', attrs).group(1) if 'title="' in attrs else 'Document'
- author = re.search(r'author="(.*?)"', attrs).group(1) if 'author="' in attrs else ''
- date = re.search(r'date="(.*?)"', attrs).group(1) if 'date="' in attrs else ''
- copyright_txt = re.search(r'copyright="(.*?)"', attrs).group(1) if 'copyright="' in attrs else ''
-
- title_block = f'''
- <div class="rt-title-block">
- <h1>{title}</h1>
- <div class="rt-meta"><span class="author">{author}</span> | <span class="date">{date}</span></div>
- <div class="rt-copyright">{copyright_txt}</div>
- </div>
- '''
- html_str = html_str[:title_match.start()] + title_block + html_str[title_match.end():]
-
- toc_match = re.search(r'<RT-TOC\s+level="(.*?)"></RT-TOC>', html_str, re.IGNORECASE)
- if toc_match:
- level = toc_match.group(1)
- headings = []
- if '-' in level:
- headings = re.findall(r'<h([123])>(.*?)</h[123]>', html_str, re.IGNORECASE)
- else:
- headings = re.findall(rf'<h({level})>(.*?)</h{level}>', html_str, re.IGNORECASE)
-
- toc_html = '<div class="toc"><div class="toc-title">Table of Contents</div><ul>'
- for lvl, text in headings:
- if "COVER SHEET" in text:
- continue
- toc_html += f'<li class="toc-h{lvl}">{text}</li>'
- toc_html += '</ul></div>'
-
- # Add page break AFTER the TOC instead of before it
- toc_html += '<div class="content-start"></div>'
-
- html_str = html_str[:toc_match.start()] + toc_html + html_str[toc_match.end():]
-
- seen_terms = set()
- def term_replace(match):
- text = match.group(1)
- l_text = text.lower()
- if l_text not in seen_terms:
- seen_terms.add(l_text)
- return f'<span class="rt-term">{text}</span>'
- else:
- return f'<span class="rt-term-plain">{text}</span>'
-
- html_str = re.sub(r'<RT-term>(.*?)</RT-term>', term_replace, html_str)
-
- seen_neos = set()
- def neo_replace(match):
- text = match.group(1)
- l_text = text.lower()
- if l_text not in seen_neos:
- seen_neos.add(l_text)
- return f'<span class="rt-neologism">{text}</span>'
- else:
- return f'<span class="rt-neologism-plain">{text}</span>'
-
- html_str = re.sub(r'<RT-neologism>(.*?)</RT-neologism>', neo_replace, html_str)
-
- HTML(string=html_str).write_pdf(out_file)
-
-uploaded_files = glob.glob('/mnt/data/user_uploads/*abstract.html')
-if not uploaded_files:
- uploaded_files = glob.glob('/mnt/data/uploads/*abstract.html')
-if not uploaded_files:
- for root, dirs, files in os.walk('/mnt/data'):
- for file in files:
- if file == 'abstract.html':
- uploaded_files.append(os.path.join(root, file))
-
-if uploaded_files:
- filepath = uploaded_files[0]
- with open(filepath, 'r', encoding='utf-8') as f:
- html_content = f.read()
-
- # One last aggressive check to ensure we absolutely strip ANY style="page-break..." strings
- # in case the earlier regex missed a specific formatting
- html_content = re.sub(r'style="[^"]*page-break-after:\s*always;?[^"]*"', '', html_content, flags=re.IGNORECASE)
-
- process_rt_html(html_content, 'abstract_no_gap.pdf')
- print("PDF successfully generated.")
-else:
- print("Could not find abstract.html in the data directory.")
<head>
<meta charset="UTF-8">
<title>RT Code Format: Lisp Addendum</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>RT code format conventions</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>File and directory naming conventions</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
+++ /dev/null
-window.RT_REPO_ROOT = "../../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/style_directory_dict.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/body_visibility_hidden.js"></script>');
<head>
<meta charset="UTF-8">
<title>C modules, namespaces, and the build lifecycle</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
--- /dev/null
+#!/usr/bin/env python3
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+developer/tool/build - Build Orchestrator Wrapper
+
+This script acts as the primary entry point for staging the project.
+It parses colon-separated arguments mapping specific namespaces to their
+intended build arguments.
+
+It enforces the structural convention where authored directories must
+be named <Namespace>.<Tool>. The script delegates execution to
+developer/tool/build_component/<Tool>.
+"""
+
+import sys, os, subprocess, fnmatch
+from typing import Dict, Tuple
+
+def check_environment() -> None:
+ setup_must_be = "developer/tool/setup"
+ current_setup = os.environ.get("SETUP", "")
+ if current_setup != setup_must_be:
+ print(f"developer/tool/build:: error: must be run in the {setup_must_be} environment", file=sys.stderr)
+ sys.exit(1)
+
+def get_namespaces() -> Dict[str, Tuple[str, str]]:
+ authored_dir = os.path.join(os.environ.get("REPO_HOME", "."), "developer", "authored")
+ if not os.path.isdir(authored_dir):
+ return {}
+
+ namespaces = {}
+ for TM_item in os.listdir(authored_dir):
+ path = os.path.join(authored_dir, TM_item)
+ if os.path.isdir(path) and not TM_item.startswith("."):
+ parts = TM_item.rsplit(".", 1)
+ if len(parts) == 2:
+ ns = parts[0]
+ tool = parts[1]
+ namespaces[ns] = (tool, TM_item)
+ else:
+ print(f"(warning) Ignoring '{TM_item}': directory lacks the required .<tool> suffix.", file=sys.stderr)
+
+ return dict(sorted(namespaces.items()))
+
+def print_usage(namespaces: Dict[str, Tuple[str, str]]) -> None:
+ print("Usage: build [<namespace_pattern>[:<argument>]*]*\n")
+ print("Arguments are passed directly to the component specified by the directory suffix.\n")
+
+ print("Available namespaces:")
+ if namespaces:
+ for TM_ns, (TM_tool, TM_raw_dir) in namespaces.items():
+ print(f" {TM_ns:<20} (Component: {TM_tool}, Dir: {TM_raw_dir})")
+ else:
+ print(" (No conforming namespaces currently found in authored/)")
+
+ print("\nExamples:")
+ print(" build (Displays this usage message)")
+ print(" build ExampleGreet (Executes 'all' via its assigned component)")
+ print(" build Ex*:library RT:all:information (Batch execution with pattern matching)")
+ print(" build \"*:clean\" (Cleans all namespaces. Quotes prevent Bash expansion)")
+
+def run_build(argument: str, namespace: str, tool: str, raw_dir: str) -> None:
+ tool_path = os.path.join("tool", "build_component", tool)
+
+ if not os.path.isfile(tool_path) or not os.access(tool_path, os.X_OK):
+ print(f"error: Build component '{tool}' for namespace '{namespace}' is missing or not executable at {tool_path}", file=sys.stderr)
+ sys.exit(1)
+
+ env = os.environ.copy()
+ env["NAMESPACE"] = namespace
+ env["NAMESPACE_DIR"] = raw_dir
+
+ tool_cmd = [tool_path, argument]
+
+ try:
+ subprocess.run(tool_cmd, env=env, check=True)
+ except subprocess.CalledProcessError as e:
+ print(f"Build component '{tool}' failed for namespace '{namespace}' with exit code {e.returncode}", file=sys.stderr)
+ sys.exit(e.returncode)
+
+def CLI() -> None:
+ check_environment()
+
+ repo_home = os.environ.get("REPO_HOME", ".")
+ dev_dir = os.path.join(repo_home, "developer")
+ os.chdir(dev_dir)
+
+ args = sys.argv[1:]
+ namespaces = get_namespaces()
+
+ if not args or args[0] in ["usage", "help", "-h", "--help"]:
+ print_usage(namespaces)
+ sys.exit(0)
+
+ matched_any = False
+ ns_keys = list(namespaces.keys())
+
+ for TM_arg in args:
+ parts = TM_arg.split(":")
+ pattern = parts[0]
+ arguments = parts[1:]
+
+ if not arguments:
+ arguments = ["all"]
+
+ matches = fnmatch.filter(ns_keys, pattern)
+
+ if not matches:
+ print(f"(warning) Pattern '{pattern}' matched no namespaces.", file=sys.stderr)
+ continue
+
+ matched_any = True
+ for TM_ns in matches:
+ tool, raw_dir = namespaces[TM_ns]
+ for TM_argument in arguments:
+ run_build(TM_argument, TM_ns, tool, raw_dir)
+
+ if not matched_any:
+ sys.exit(1)
+
+ print("build done.")
+
+if __name__ == "__main__":
+ CLI()
--- /dev/null
+#!/usr/bin/env bash
+# developer/tool/copy
+
+set -euo pipefail
+CMD="${1:-all}"
+
+if [ "$CMD" = "clean" ]; then
+ rm -rf "scratchpad/made/$NAMESPACE"
+ echo "copy: Cleaned scratchpad/made/$NAMESPACE"
+elif [ "$CMD" = "all" ]; then
+ mkdir -p "scratchpad/made"
+ cp -au "authored/$NAMESPACE_DIR" "scratchpad/made/$NAMESPACE"
+ echo "copy: Staged $NAMESPACE_DIR -> scratchpad/made/$NAMESPACE"
+else
+ echo "copy: Unrecognized command '$CMD'"
+ exit 1
+fi
--- /dev/null
+#!/usr/bin/env bash
+# developer/tool/build_component/make
+
+set -euo pipefail
+CMD="${1:-all}"
+
+# The GNU Make orchestrator expects C_SOURCE_DIR to point to the raw directory
+export C_SOURCE_DIR="authored/$NAMESPACE_DIR"
+export KMOD_SOURCE_DIR="authored/$NAMESPACE_DIR"
+
+# Isolate build products into their specific namespace directory
+export LIBRARY_DIR="scratchpad/made/$NAMESPACE"
+export MACHINE_DIR="scratchpad/made/$NAMESPACE"
+
+/bin/make -f tool/build_component/makefile "$CMD"
--- /dev/null
+# developer/tool/build_component/makefile
+.SUFFIXES:
+.EXPORT_ALL_VARIABLES:
+
+RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile
+
+.PHONY: usage
+usage:
+ @printf "Usage: make [usage|version|information|all|lib|CLI|kmod|clean]\n"
+
+.PHONY: version
+version:
+ @printf "local ----------------------------------------\n"
+ @echo tool/makefile version 2.0
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk version
+ @printf "target_kernel-module.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk version
+
+.PHONY: information
+information:
+ @printf "local ----------------------------------------\n"
+ -@echo CURDIR='$(CURDIR)'
+ @echo REPO_HOME="$(REPO_HOME)"
+ @echo NAMESPACE="$(NAMESPACE)"
+ @echo C_SOURCE_DIR="$(C_SOURCE_DIR)"
+ @echo KMOD_BUILD_DIR="/lib/modules/$(shell uname -r)/build"
+ @echo CURDIR="$(CURDIR)"
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk information
+ @printf "target_kernel-module.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk information
+
+.PHONY: all
+all: library CLI kmod
+
+.PHONY: library lib
+library lib:
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk library
+
+.PHONY: CLI
+CLI:
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk CLI
+
+.PHONY: kmod
+kmod:
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk kmod
+
+.PHONY: clean
+clean:
+ @printf "local ----------------------------------------\n"
+ @printf "Harmony.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk clean
+ @printf "target_kernel-module.mk ----------------------------------------\n"
+ @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk clean
+++ /dev/null
-#!/usr/bin/env bash
-script_afp=$(realpath "${BASH_SOURCE[0]}")
-
-# input guards
-
- setup_must_be="developer/tool/setup"
- if [ "$SETUP" != "$setup_must_be" ]; then
- echo "$(script_fp):: error: must be run in the $setup_must_be environment"
- exit 1
- fi
-
-set -e
-set -x
-
- cd "$REPO_HOME"/developer || exit 1
- # /bin/make -f tool/makefile $@
-
- scratchpad clear
- make
- release clean
- release write
-
-set +x
-echo "$(script_fn) done."
+++ /dev/null
-#!/usr/bin/env bash
-script_afp=$(realpath "${BASH_SOURCE[0]}")
-
-# input guards
-
- setup_must_be="developer/tool/setup"
- if [ "$SETUP" != "$setup_must_be" ]; then
- echo "$(script_fp):: error: must be run in the $setup_must_be environment"
- exit 1
- fi
-
-set -e
-set -x
-
- cd "$REPO_HOME"/developer || exit 1
- # /bin/make -f tool/makefile $@
-
- scratchpad clear
- make
- release clean
- release write
-
-set +x
-echo "$(script_fn) done."
+++ /dev/null
-#!/usr/bin/env python3
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-developer/tool/make - Build Orchestrator Wrapper
-
-This script acts as the primary entry point for compiling the project.
-It parses colon-separated arguments mapping specific namespace patterns
-(including file globs) to their intended build commands.
-
-A program developer can modify this script to add custom pre-build checks,
-custom argument parsing, or project-specific routing logic.
-"""
-
-import sys ,os ,subprocess ,fnmatch
-from typing import List
-
-def check_environment() -> None:
- setup_must_be = "developer/tool/setup"
- current_setup = os.environ.get("SETUP" ,"")
- if( current_setup != setup_must_be ):
- print(f"developer/tool/make:: error: must be run in the {setup_must_be} environment" ,file=sys.stderr)
- sys.exit(1)
-
-def get_namespaces() -> List[str]:
- authored_dir = os.path.join(os.environ.get("REPO_HOME" ,".") ,"developer" ,"authored")
- if( not os.path.isdir(authored_dir) ):
- return []
-
- namespaces = []
- for TM_name in os.listdir(authored_dir):
- path = os.path.join(authored_dir ,TM_name)
- if( os.path.isdir(path) and not TM_name.startswith(".") ):
- namespaces.append(TM_name)
- namespaces.sort()
- return namespaces
-
-def print_usage() -> None:
- print("Usage: make [<namespace_pattern>[:<command>]*]*\n")
- print("Commands:")
- print(" all Build library, CLI, and kmod (Default)")
- print(" library Build only the library archives")
- print(" CLI Build only the CLI executables")
- print(" kmod Build kernel modules")
- print(" clean Remove build artifacts")
- print(" information Display build environment variables\n")
-
- print("Available namespaces:")
- namespaces = get_namespaces()
- if( namespaces ):
- for TM_ns in namespaces:
- print(f" {TM_ns}")
- else:
- print(" (No namespaces currently found in authored/)")
-
- print("\nExamples:")
- print(" make (Displays this usage message)")
- print(" make ExampleGreet (Builds 'all' for ExampleGreet)")
- print(" make Ex*:library G*:all:information (Builds 'library' for Ex*, 'all' and 'information' for G*)")
- print(" make \"*:clean\" (Cleans all namespaces. Quotes prevent Bash expansion)")
-
-def run_make(cmd: str ,namespace: str) -> None:
- env = os.environ.copy()
- env["NAMESPACE"] = namespace
-
- make_cmd = ["/bin/make" ,"-f" ,"tool/makefile" ,cmd]
-
- try:
- subprocess.run(make_cmd ,env=env ,check=True)
- except subprocess.CalledProcessError as e:
- print(f"make failed for namespace '{namespace}' with exit code {e.returncode}" ,file=sys.stderr)
- sys.exit(e.returncode)
-
-def CLI() -> None:
- check_environment()
-
- repo_home = os.environ.get("REPO_HOME" ,".")
- dev_dir = os.path.join(repo_home ,"developer")
- os.chdir(dev_dir)
-
- args = sys.argv[1:]
-
- if( not args or args[0] in ["usage" ,"help" ,"-h" ,"--help"] ):
- print_usage()
- sys.exit(0)
-
- namespaces = get_namespaces()
- matched_any = False
-
- for TM_arg in args:
- parts = TM_arg.split(":")
- pattern = parts[0]
- commands = parts[1:]
-
- if( not commands ):
- commands = ["all"]
-
- matches = fnmatch.filter(namespaces ,pattern)
-
- if( not matches ):
- print(f"(warning) Pattern '{pattern}' matched no namespaces." ,file=sys.stderr)
- continue
-
- matched_any = True
- for TM_ns in matches:
- for TM_cmd in commands:
- run_make(TM_cmd ,TM_ns)
-
- if( not matched_any ):
- sys.exit(1)
-
- print("make done.")
-
-if __name__ == "__main__":
- CLI()
+++ /dev/null
-# developer/tool/makefile - Orchestrator (Hybrid)
-.SUFFIXES:
-.EXPORT_ALL_VARIABLES:
-
-RT_MAKEFILE_DP := $(REPO_HOME)/shared/tool/makefile
-
-# If a namespace is provided, update the source directory
-ifneq ($(NAMESPACE),)
- C_SOURCE_DIR := authored/$(NAMESPACE)
- export C_SOURCE_DIR
-endif
-
-.PHONY: usage
-usage:
- @printf "Usage: make [usage|version|information|all|lib|CLI|kmod|clean]\n"
-
-.PHONY: version
-version:
- @printf "local ----------------------------------------\n"
- @echo tool/makefile version 2.0
- @printf "Harmony.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk version
- @printf "target_kernel-module.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk version
-
-.PHONY: information
-information:
- @printf "local ----------------------------------------\n"
- -@echo CURDIR='$(CURDIR)'
- @echo REPO_HOME="$(REPO_HOME)"
- @echo NAMESPACE="$(NAMESPACE)"
- @echo C_SOURCE_DIR="$(C_SOURCE_DIR)"
- @echo KMOD_BUILD_DIR="/lib/modules/$(shell uname -r)/build"
- @echo CURDIR="$(CURDIR)"
- @printf "Harmony.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk information
- @printf "target_kernel-module.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk information
-
-.PHONY: all
-all: library CLI kmod
-
-.PHONY: library lib
-library lib:
- @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk library
-
-.PHONY: CLI
-CLI:
- @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk CLI
-
-.PHONY: kmod
-kmod:
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk kmod
-
-.PHONY: clean
-clean:
- @printf "local ----------------------------------------\n"
- @printf "Harmony.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/Harmony.mk clean
- @printf "target_kernel-module.mk ----------------------------------------\n"
- @$(MAKE) -f $(RT_MAKEFILE_DP)/target_kernel-module.mk clean
<head>
<meta charset="UTF-8">
<title>RT semantic HTML tags</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
-The `Harmony` skeletong is setup to use `RT-style-JS_public`, it only requires that the `RT-style-JS_public` be installed to the side, at the same directory level, as the `Harmony` project.
+The `Harmony` skeletong is setup to use `RT-style`, it only requires that the `RT-style` be installed to the side, at the same directory level, as the `Harmony` project.
-`RT-style-JS_public` can be used with a standalone html document
+`RT-style` can be used with a standalone html document
1. cd to the directory with the standalone document
-2. install RT-style-JS_public
- 1.1 git clone https://github.com/Thomas-Walker-Lynch/RT-style-JS_public.git
- 1.2 enter RT-style-JS:
+2. install RT-style
+ 1.1 git clone https://github.com/Thomas-Walker-Lynch/RT-style.git
+ 1.2 enter RT-style:
> bash
> . setup developer
> make
window.RT_REPO_ROOT = "../";
document.write('<script src="' + 'dictionary_style-directory.js"></script>');
- document.write('<script src="' + 'RT-style-JS_public/consumer/release/RT/core/loader.js"></script>');
- document.write('<script src="' + 'RT-style-JS_public/consumer/release/RT/core/body_visibility_hidden.js"></script>');
+ document.write('<script src="' + 'RT-style/consumer/release/RT/core/loader.js"></script>');
+ document.write('<script src="' + 'RT-style/consumer/release/RT/core/body_visibility_hidden.js"></script>');
--------------------------------------------------------------------------------
dictionary_style-directory.js 2026-05-20T06:49:14Z
window.StyleRT_namespaces = {
- "RT": "RT-style-JS_public/consumer/release/RT"
+ "RT": "RT-style/consumer/release/RT"
,"Project": "style"
};
<head>
<meta charset="UTF-8">
<title>Introduction to Harmony</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
</p>
<p>
- Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style-JS_public</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
+ Currently, our developers write documents directly in HTML using the RT semantic tags. See the <RT-code>RT-style</RT-code> project and the documentation there. A common approach is to copy another document and the <RT-code>setup.js</RT-code> file, then to type over the top of that other document. Only one <RT-code>setup.js</RT-code> file is used per directory. Be sure to edit the relative root path found at the top of <RT-code>setup.js</RT-code>. Plain text, emacs org, and mark down have all been used in prior versions of Harmony.
</p>
<h2>Untracked directories</h2>
│ ├── made
│ ├── dictionary_style-directory.js
│ ├── third_party
- │ │ ├── RT-style-JS_public -> ../../../RT-style-JS_public/
+ │ │ ├── RT-style -> ../../../RT-style/
│ │ └── upstream
│ └── tool
│ ├── scratchpad
<head>
<meta charset="UTF-8">
<title>Product development roles and workflow</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>Product maintenance roles and workflow</title>
- <script src="setup.js"></script>
<script>
- window.StyleRT.include('RT/theme');
- window.StyleRT.include('RT/layout/article_tech_ref');
+ window.RT = window.RT || {};
+ window.RT.dirpr_library = "RT";
+ document.write('<script src="' + window.RT.dirpr_library + '/core/loader.js"></' + 'script>');
+ </script>
+ <script>
+ window.RT.load('core/utility');
+ window.RT.load('core/block_visibility_during_layout');
+ window.RT.load('theme');
+ window.RT.load('layout/article_tech_ref');
</script>
</head>
<body>
+++ /dev/null
-window.RT_REPO_ROOT = "../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/style_directory_dict.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'developer/authored/RT/core/body_visibility_hidden.js"></script>');
+++ /dev/null
-
- body {
- font-family: 'Noto Sans JP', Arial, sans-serif;
- background-color: hsl(0, 0%, 0%);
- color: hsl(42, 100%, 80%);
- padding: 2rem;
- }
- .page {
- padding: 3rem;
- margin: 1.25rem auto;
- max-width: 46.875rem;
- background-color: hsl(0, 0%, 0%);
- box-shadow: 0 0 0.625rem hsl(42, 100%, 50%);
- }
- h1 {
- font-size: 1.5rem;
- text-align: center;
- color: hsl(42, 100%, 84%);
- text-transform: uppercase;
- margin-top: 1.5rem;
- }
- h2 {
- font-size: 1.25rem;
- color: hsl(42, 100%, 84%);
- text-align: center;
- margin-top: 2rem;
- }
- h3 {
- font-size: 1.125rem;
- color: hsl(42, 100%, 75%);
- margin-top: 1.5rem;
- }
- p, li {
- color: hsl(42, 100%, 90%);
- text-align: justify;
- margin-bottom: 1rem;
- }
- code {
- font-family: 'Courier New', Courier, monospace;
- background-color: hsl(0, 0%, 25%);
- padding: 0.125rem 0.25rem;
- color: hsl(42, 100%, 90%);
- }
-
# setup the role
#
export ROLE="${1}"
+ export ROLE_HOME="$REPO_HOME/$ROLE"
+ echo ROLE_HOME "$ROLE_HOME"
- tool="${REPO_HOME}/${ROLE}/tool"
+ tool="${ROLE_HOME}/tool"
if [[ ":${PATH}:" != *":${tool}:"* ]]; then
export PATH="${tool}:${PATH}"
fi
+++ /dev/null
-window.RT_REPO_ROOT = "../../";
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/dictionary_style-directory.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/loader.js"></script>');
-document.write('<script src="' + window.RT_REPO_ROOT + 'shared/linked-project/RT-style-JS_public/consumer/release/RT/core/body_visibility_hidden.js"></script>');
repo_home = Path(repo_home_env)
# Define the canonical target location
- target_src = repo_home / 'shared' / 'linked-project' / 'RT-style-JS_public' / 'consumer' / 'release' / 'RT'
+ target_src = repo_home / 'shared' / 'linked-project' / 'RT-style' / 'consumer' / 'release' / 'RT'
# Determine the destination directory
if len(sys.argv) > 1:
export PATH
-#--------------------------------------------------------------------------------
-# used by release scripts
-#
- install_file() {
- if [ "$#" -lt 3 ]; then
- echo "env::install_file usage: install_file <source1> <source2> ... <target_dir> <permissions>"
- return 1
- fi
-
- perms="${@: -1}" # Last argument is permissions
- target_dp="${@: -2:1}" # Second-to-last argument is the target directory
- sources=("${@:1:$#-2}") # All other arguments are source files
-
- if [ ! -d "$target_dp" ]; then
- echo "env::install_file no install done: target directory '$target_dp' does not exist."
- return 1
- fi
-
- for source_fp in "${sources[@]}"; do
- if [ ! -f "$source_fp" ]; then
- echo "env::install_file: source file '$source_fp' does not exist."
- return 1
- fi
-
- target_file="$target_dp/$(basename "$source_fp")"
-
- if ! install -m "$perms" "$source_fp" "$target_file"; then
- echo "env::install_file: Failed to install $(basename "$source_fp") to $target_dp"
- return 1
- fi
- done
- }
-
- export -f install_file
-
# --------------------------------------------------------------------------------
# closing
#
-echo "RT-style-JS_public v3.1 2026-06-20 10:39:27 Z"
+echo "RT-style v3.1 2026-06-20 10:39:27 Z"
echo "Harmony v3.3 2026-06-21 13:20:18 Z"