--- /dev/null
+// RT-style.js (Internal RT-style project router)
+window.RT = window.RT || {};
+
+(function() {
+ const project_name = "RT-style";
+ const path = window.location.pathname;
+ const project_root_index = path.indexOf('/' + project_name + '/');
+
+ if (project_root_index !== -1) {
+ // substring(0, x) excludes the trailing slash. We must prepend it to the payload.
+ const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1);
+ window.RT.dirpr_library = absolute_project_root + "/consumer/made/Manuscript";
+ } else {
+ // Fallback for when served via local Python HTTP daemon from the project root
+ window.RT.dirpr_library = "../consumer/made/Manuscript";
+ }
+
+ document.write('<script src="' + window.RT.dirpr_library + '/Core/loader.js"><\/script>');
+
+ document.write(
+ '<script>' +
+ 'window.RT.load("Core/utility");' +
+ 'window.RT.load("Core/block_visibility_during_layout");' +
+ 'window.RT.load("Theme");' +
+ 'window.RT.load("Element/theme_selector");' +
+ '<\/script>'
+ );
+})();
<head>
<meta charset="UTF-8">
<title>Release howto</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
--- /dev/null
+#+TITLE: Installing Python in Harmony
+#+AUTHOR: Thomas Walker Lynch
+#+OPTIONS: toc:2 num:nil
+
+* Overview
+
+This document describes how to install a project-local Python environment under:
+
+#+begin_src bash
+shared/linked-project/Python
+#+end_src
+
+* Precondition
+
+Ensure the following:
+
+- You are in a POSIX shell with =python3= installed.
+- The =python3-venv= package is available (on Debian: =sudo apt install python3-venv=).
+- You have sourced the Harmony environment via =env_toolsmith= to initialize =REPO_HOME= and related variables.
+
+* Step-by-Step Installation
+
+1. Source the Harmony environment:
+ #+begin_src bash
+ source env_toolsmith
+ #+end_src
+
+2. Create the virtual environment:
+ #+begin_src bash
+ python3 -m venv "$REPO_HOME/shared/linked-project/Python"
+ #+end_src
+
+3. Activate it temporarily to install required packages:
+ #+begin_src bash
+ source "$REPO_HOME/shared/linked-project/Python/bin/activate"
+ pip install --upgrade pip
+ pip install pytest # Add any shared packages here
+ deactivate
+ #+end_src
+
+4. Rename Python's default activate and deactivate:
+ Harmony provides its own role-aware environment management. Using Python’s default activation scripts may interfere with prompt logic, PATH order, and role-specific behavior.
+
+ Disable the default scripts by renaming them:
+ #+begin_src bash
+ mv "$REPO_HOME/shared/linked-project/Python/bin/activate" \
+ "$REPO_HOME/shared/linked-project/Python/bin/activate_deprecated"
+ #+end_src
+
+ This ensures that accidental sourcing of Python’s =activate= script won't override Harmony's environment setup.
+
+5. Verify installation:
+ #+begin_src bash
+ ls "$REPO_HOME/shared/linked-project/Python/bin/python3"
+ #+end_src
+
+ The binary should exist and report a working Python interpreter when run.
+
+* Notes
+
+- The virtual environment is deliberately named =Python=, not =venv=, to reflect its role as a shared system component.
+- Harmony environment scripts define and control =VIRTUAL_ENV=, =PYTHON_HOME=, and =PATH=, making Python activation seamless and uniform.
+- There is no need to use Python’s =bin/activate= directly — it is fully replaced by Harmony’s environment logic.
+
+* Related Files
+
+- =shared/authored/env=
+- =shared/authored/env_source=
+- =env_developer=, =env_tester=, =env_toolsmith=
+
+* Last Verified
+
+2025-05-19 :: Activate/deactivate renamed post-install. Requires Harmony environment sourcing prior to execution.
--- /dev/null
+
+This is the generic install.org doc that comes with the skeleton.
+
+1. $REPO_HOME/shared/linked-project/.gitignore:
+
+ *
+ !/.gitignore
+ !/patch
+
+ The only things from the third party directory that will be pushed to the repo origin is the .gitignore file and the patches.
+
+
+2. downloaded tar files etc. go into the directory `upstream`
+
+ $REPO_HOME/shared/upstream
+
+ Typically the contents of upstream are deleted after the install.
+
+3. for the base install
+
+ cd $REPO_HOME/shared/linked-project
+ do whatever it takes to install tool, as examples:
+ git clone <tool_path>
+ tar -xzf ../upstream/tar
+ ...
+
+ Be sure to add the path to the tool executable(s) in the $REPO_HOME/env_$ROLE files for the $ROLE who uses the tool.
+
+ Assuming you are not also developing the tool, for safety
+ change each installed git project to a local branch:
+
+ b=<site>_<project>_local_$USER
+ git switch -c "$b"
+
+
+4. Define some variables to simplify our discussion. Lowercase variable names
+ are not exported from the shell.
+
+ # already set in the environment
+ # REPO_HOME
+ # PROJECT
+ # USER
+
+ # example tool names: 'RT_gcc' 'RT-project share` etc.
+ tool=<tool-name>
+ tool_dpath="$REPO_HOME/shared/linked-project/$tool"
+ patch_dpath="$REPO_HOME/shared/patch/"
+
+
+5. create a patch series (from current vendor state → your local edits)
+
+ # this can be repeated and will create an encompassing diff file
+
+ # optionally crate a new branch after cloning the third party tool repo and work from there. You won't make any commits, but in case you plan to ever check the changes in, or have a the bad habit of doing ommits burned into your brain-stem, making a brnch will help.
+
+ # make changes
+
+ cd "$tool_dpath"
+
+ # do your edits
+
+ # Stage edits. Do not commit them!! Be sure you are in the third party
+ # tool directory when doing `git add -A` and `git diff` commands.
+ git add -A
+
+ # diff the stage from the current repo to create the patch file
+ git diff --staged > "$patch_dpath/$tool"
+
+ # the diff file can be added to the project and checked in at the project level.
+
+
+6. how to apply an existing patch
+
+ Get a fresh clone of the tool into $tool_dpath.
+
+ cd "$tool_dpath"
+ git apply "$patch_dpath/$tool"
+
+ You can see what `git apply` would do by running
+
+ git apply --check /path/to/your/patch_dpath/$tool
+++ /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-style";
- 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
+// 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 || {};
+
+// 1. Establish the module registry
+window.RT._loaded_modules = window.RT._loaded_modules || new Set();
+
+window.RT.load = function(module_path){
+ let target_module = module_path;
+
+ // Strict enforcement of the PascalCase namespace
+ 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;
+ }
+
+ // 2. The Idempotency Check: Abort if already loaded
+ if (window.RT._loaded_modules.has(target_module)) {
+ return;
+ }
+
+ // 3. Register the module
+ window.RT._loaded_modules.add(target_module);
+
+ 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
+// for documents in RT-style/Manuscript/document
+window.RT = window.RT || {};
+
+(function() {
+ // We are the style library, so ...
+ window.RT.dirpr_library = "..";
+
+ // 1. Inject the loader script
+ document.write('<script src="' + window.RT.dirpr_library + '/Core/loader.js"><\/script>');
+
+ // 2. Inject a secondary script block for the core dependencies
+ document.write(
+ '<script>' +
+ 'window.RT.load("Core/utility");' +
+ 'window.RT.load("Core/block_visibility_during_layout");' +
+ 'window.RT.load("Theme");' +
+ 'window.RT.load("Element/theme_selector");' +
+ '<\/script>'
+ );
+})();
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <title>Your Document Title</title>
+
+ <script src="RT-style.js"></script>
+ <script>
+ window.RT.load('Layout/article_tech_ref');
+ </script>
+ </head>
+ <body>
+ <rt-theme-selector></rt-theme-selector>
+
+ <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/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
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+"""
+RTfmt — Reasoning Technology code formatter (Predicate Tokenizer)
+
+Commands:
+ RTfmt write [--lisp] <file ...> Format files in place (rewrite originals)
+ RTfmt copy [--lisp] <file ...> Save backups as <file>~ then format originals
+ RT-formatter pipe [--lisp] Read from stdin, write to stdout
+ RTfmt self_test Run built-in tests
+ RTfmt version Show tool version
+ RTfmt help | --help Show usage
+"""
+
+import sys ,re ,shutil ,os
+from typing import List ,Tuple ,Optional ,TextIO
+
+RTF_VERSION = "0.5.0-predicate"
+
+def get_usage() -> str:
+ prog_name = os.path.basename(sys.argv[0])
+ return f"""\
+Usage:
+ {prog_name} write [--lisp] <file ...>
+ {prog_name} copy [--lisp] <file ...>
+ {prog_name} pipe [--lisp]
+ {prog_name} self_test
+ {prog_name} version
+ {prog_name} help | --help
+"""
+
+# Removed < and > so they are treated as standard CODE operators
+BR_OPEN = "([{"
+BR_CLOSE = ")]}"
+PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
+REV = dict( zip(BR_CLOSE ,BR_OPEN) )
+
+# --------------- Lexer ----------------
+
+class RT_Token:
+ def __init__(self ,kind: str ,text: str):
+ self.kind = kind
+ self.text = text
+
+ def __repr__(self):
+ return f"<{self.kind}:{repr(self.text)}>"
+
+TOKEN_REGEX = re.compile(
+ r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
+ r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
+ r'|(?P<SPACE>[ \t]+)'
+ r'|(?P<NEWLINE>\n)'
+ r'|(?P<COMMA>,)'
+ r'|(?P<BR_OPEN>[\[\(\{])'
+ r'|(?P<BR_CLOSE>[\]\)\}])'
+ r'|(?P<CODE>[^ \t\n,\[\(\{\]\)\}"\'#/]+|/)'
+)
+
+def tokenize(text: str) -> List[RT_Token]:
+ tokens = []
+ for TM_match in TOKEN_REGEX.finditer(text):
+ kind = TM_match.lastgroup
+ text_val = TM_match.group(kind)
+ tokens.append( RT_Token(kind ,text_val) )
+ return tokens
+
+# --------------- Intelligence API ----------------
+
+class TokenStream:
+ def __init__(self ,tokens: List[RT_Token]):
+ self.tokens = tokens
+
+ def get_token(self ,index: int) -> Optional[RT_Token]:
+ if 0 <= index < len(self.tokens):
+ return self.tokens[index]
+ return None
+
+ def next_sig_index(self ,index: int) -> Optional[int]:
+ for TM_i in range(index + 1 ,len(self.tokens)):
+ if self.tokens[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
+ return TM_i
+ return None
+
+ def is_first_on_line(self ,index: int) -> bool:
+ for TM_i in range(index - 1 ,-1 ,-1):
+ k = self.tokens[TM_i].kind
+ if k == "NEWLINE":
+ return True
+ if k != "SPACE":
+ return False
+ return True # Start of file
+
+ def indent_of_line(self ,index: int) -> str:
+ for TM_i in range(index ,-1 ,-1):
+ if self.tokens[TM_i].kind == "NEWLINE":
+ if TM_i + 1 < len(self.tokens) and self.tokens[TM_i + 1].kind == "SPACE":
+ return self.tokens[TM_i + 1].text
+ return ""
+ if self.tokens and self.tokens[0].kind == "SPACE":
+ return self.tokens[0].text
+ return ""
+
+ def indent_of_left_match(self ,index: int) -> Optional[str]:
+ tok = self.get_token(index)
+ if not tok or tok.kind != "BR_CLOSE":
+ return None
+ target_opener = REV[tok.text]
+ depth = 0
+ for TM_i in range(index - 1 ,-1 ,-1):
+ t = self.tokens[TM_i]
+ if t.kind == "BR_CLOSE":
+ depth += 1
+ elif t.kind == "BR_OPEN":
+ if depth > 0:
+ depth -= 1
+ elif t.text == target_opener:
+ return self.indent_of_line(TM_i)
+ return None
+
+# --------------- Rule Engine ----------------
+
+def rule_migrate_vertical_commas(stream: TokenStream):
+ TM_i = 0
+ while TM_i < len(stream.tokens):
+ if stream.tokens[TM_i].kind == "COMMA":
+ is_trailing = False
+ next_sig = stream.next_sig_index(TM_i)
+ if next_sig is not None:
+ for TM_j in range(TM_i + 1 ,next_sig):
+ if stream.tokens[TM_j].kind == "NEWLINE":
+ is_trailing = True
+ break
+
+ if is_trailing:
+ comma_tok = stream.tokens.pop(TM_i)
+ next_sig -= 1 # Shifted because of pop
+ stream.tokens.insert(next_sig ,comma_tok)
+ continue
+ TM_i += 1
+
+def rule_format_horizontal_commas(stream: TokenStream):
+ for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+ if stream.tokens[TM_i].kind == "COMMA":
+ if stream.is_first_on_line(TM_i):
+ continue
+
+ next_tok = stream.get_token(TM_i + 1)
+ if next_tok and next_tok.kind == "SPACE":
+ stream.tokens.pop(TM_i + 1)
+
+ prev_tok = stream.get_token(TM_i - 1)
+ if prev_tok and prev_tok.kind == "SPACE":
+ if prev_tok.text != " ":
+ prev_tok.text = " "
+ else:
+ stream.tokens.insert(TM_i ,RT_Token("SPACE" ," "))
+
+def rule_fix_closing_indent(stream: TokenStream):
+ for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+ if stream.tokens[TM_i].kind == "BR_CLOSE" and stream.is_first_on_line(TM_i):
+ target_indent = stream.indent_of_left_match(TM_i)
+ if target_indent is not None:
+ prev = stream.get_token(TM_i - 1)
+ if prev and prev.kind == "SPACE":
+ prev.text = target_indent
+ else:
+ stream.tokens.insert(TM_i ,RT_Token("SPACE" ,target_indent))
+
+def rule_tighten_brackets(stream: TokenStream):
+ for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
+ if stream.tokens[TM_i].kind == "SPACE" and not stream.is_first_on_line(TM_i):
+ prev_t = stream.get_token(TM_i - 1)
+ next_t = stream.get_token(TM_i + 1)
+ if (prev_t and prev_t.kind == "BR_OPEN") or (next_t and next_t.kind == "BR_CLOSE"):
+ stream.tokens.pop(TM_i)
+
+def get_bracket_spans(stream: TokenStream) -> List[Tuple[int ,int]]:
+ stack = []
+ spans = []
+ for TM_i ,tok in enumerate(stream.tokens):
+ if tok.kind == "BR_OPEN":
+ stack.append( (tok.text ,TM_i) )
+ elif tok.kind == "BR_CLOSE":
+ if stack and REV[tok.text] == stack[-1][0]:
+ _ ,pos = stack.pop()
+ if not stack:
+ spans.append( (pos ,TM_i) )
+ return spans
+
+def rule_pad_outermost(stream: TokenStream ,is_lisp: bool):
+ if is_lisp:
+ return
+ while True:
+ spans = get_bracket_spans(stream)
+ changed = False
+ for TM_start ,TM_end in reversed(spans):
+ has_inner = False
+ for TM_k in range(TM_start + 1 ,TM_end):
+ if stream.tokens[TM_k].kind in ("BR_OPEN" ,"BR_CLOSE"):
+ has_inner = True
+ break
+
+ if has_inner:
+ left_has = (TM_start + 1 < len(stream.tokens)) and stream.tokens[TM_start + 1].kind == "SPACE"
+ right_has = (TM_end - 1 >= 0) and stream.tokens[TM_end - 1].kind == "SPACE"
+ if not left_has or not right_has:
+ if not right_has:
+ stream.tokens.insert(TM_end ,RT_Token("SPACE" ," "))
+ if not left_has:
+ stream.tokens.insert(TM_start + 1 ,RT_Token("SPACE" ," "))
+ changed = True
+ break
+ if not changed:
+ break
+
+# --------------- Public API ----------------
+
+def format_tokens(tokens: List[RT_Token] ,is_lisp: bool) -> str:
+ stream = TokenStream(tokens)
+
+ rule_migrate_vertical_commas(stream)
+ rule_format_horizontal_commas(stream)
+ rule_tighten_brackets(stream)
+ rule_fix_closing_indent(stream)
+ rule_pad_outermost(stream ,is_lisp)
+
+ return "".join(t.text for t in stream.tokens)
+
+def rt_format_text(text: str ,is_lisp: bool) -> str:
+ tokens = tokenize(text)
+ return format_tokens(tokens ,is_lisp)
+
+def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
+ text = inp.read()
+ out.write( rt_format_text(text ,is_lisp) )
+
+# --------------- Self-test ----------------
+
+def run_self_test() -> bool:
+ ok = True
+ def chk(src ,exp):
+ nonlocal ok
+ got = rt_format_text(src ,False)
+ if got != exp:
+ print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
+ ok = False
+
+ chk("a,b,c" ,"a ,b ,c")
+ chk("a , b , c" ,"a ,b ,c")
+ chk(" ,vertical_arg" ," ,vertical_arg")
+
+ chk("int a=0,\n b=1,\n c=2;" ,"int a=0\n ,b=1\n ,c=2;")
+
+ chk("f ( x )" ,"f(x)")
+ chk("f(x) + g(y)" ,"f(x) + g(y)")
+ chk(" {" ," {")
+
+ src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
+ exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
+ chk(src ,exp)
+
+ chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
+
+ # Operator protection check
+ chk("for(int TM = 0; TM < count; ++TM)" ,"for(int TM = 0; TM < count; ++TM)")
+
+ print("SELFTEST OK" if ok else "SELFTEST FAILED")
+ return ok
+
+# --------------- CLI ----------------
+def write_files(paths: List[str] ,is_lisp: bool) -> int:
+ for TM_path in paths:
+ with open(TM_path ,"r" ,encoding="utf-8") as f:
+ data = f.read()
+
+ formatted = rt_format_text(data ,is_lisp)
+
+ # Only touch the file if the content actually changed
+ if data != formatted:
+ with open(TM_path ,"w" ,encoding="utf-8") as f:
+ f.write(formatted)
+ print(f"Formatted: {TM_path}")
+ return 0
+
+def copy_files(paths: List[str] ,is_lisp: bool) -> int:
+ for TM_path in paths:
+ shutil.copy2(TM_path ,TM_path + "~")
+ return write_files(paths ,is_lisp)
+
+def CLI(argv=None) -> int:
+ args = list(sys.argv[1:] if argv is None else argv)
+ usage_text = get_usage()
+
+ if not args or args[0] in {"help" ,"--help" ,"-h"}:
+ print(usage_text)
+ return 0
+
+ is_lisp = "--lisp" in args
+ args = [TM_a for TM_a in args if TM_a != "--lisp"]
+
+ if not args:
+ return 0
+
+ cmd = args[0]
+ rest = args[1:]
+
+ if cmd == "version":
+ print(RTF_VERSION)
+ return 0
+ if cmd == "self_test":
+ ok = run_self_test()
+ return 0 if ok else 1
+ if cmd == "pipe":
+ rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
+ return 0
+ if cmd == "write":
+ if not rest:
+ print("write: missing <file ...>\n" + usage_text)
+ return 2
+ return write_files(rest ,is_lisp)
+ if cmd == "copy":
+ if not rest:
+ print("copy: missing <file ...>\n" + usage_text)
+ return 2
+ return copy_files(rest ,is_lisp)
+
+ print(f"Unknown command: {cmd}\n" + usage_text)
+ return 2
+
+if __name__ == "__main__":
+ sys.exit( CLI() )
--- /dev/null
+(defun RT-formatter-buffer ()
+ "Format the current buffer using RTfmt."
+ (interactive)
+ (if (not (executable-find "RT-formatter"))
+ (message "Error: RTfmt executable not found in PATH.")
+ (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
+ (args (list "pipe")))
+ (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
+ (setq args (append args (list "--lisp"))))
+ (unwind-protect
+ (let ((exit-code (apply #'call-process-region
+ (point-min) (point-max)
+ "RT-formatter"
+ nil temp-buffer nil
+ args)))
+ (if (zerop exit-code)
+ ;; Check if the formatted text is actually different
+ (if (= (compare-buffer-substrings nil nil nil temp-buffer nil nil) 0)
+ (message "RTfmt: Already perfectly formatted.")
+ (replace-buffer-contents temp-buffer)
+ (message "RT-formatter formatting successful."))
+ (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
+ (kill-buffer temp-buffer)))))
--- /dev/null
+// commas and simple tight brackets
+int g(){
+ int a=0 ,
+ b=1 ,
+ c=2;
+ return h(a ,b ,c);
+}
+
+// balanced outermost-with-nesting -> pad inside outer ()
+int f(){ return outer(inner(a ,b)); }
+
+// strings and comments must be unchanged
+int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
+
+// unbalanced open-right with nesting -> pad after first unmatched '('
+int u(){if(doit(foo(1 ,2) // missing ))
+ return 0;}
+
+// arrays / subscripts stay tight; commas still RT-style
+int a(int i ,int j){ return M[i ,j] + V[i] + W[j]; }
--- /dev/null
+# commas and spacing in defs / calls
+def f ( x , y , z ):
+ return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
+
+# outermost-with-nesting -> pad inside outer ()
+val = outer( inner( a,b ) )
+
+# strings/comments untouched
+s = "text, with , commas ( not to touch )" # a ,b ,c
+
+# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
+def g():
+ return result) # likely unchanged
+
+# unbalanced: open-right (first unmatched opener) with inner bracket following
+k = compute(x, f(y
+++ /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="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>RT code format conventions</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
--- /dev/null
+// RT-style.js (Internal RT-style project router)
+window.RT = window.RT || {};
+
+(function() {
+ const project_name = "RT-style";
+ const path = window.location.pathname;
+ const project_root_index = path.indexOf('/' + project_name + '/');
+
+ if (project_root_index !== -1) {
+ // substring(0, x) excludes the trailing slash. We must prepend it to the payload.
+ const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1);
+ window.RT.dirpr_library = absolute_project_root + "/consumer/made/Manuscript";
+ } else {
+ // Fallback for when served via local Python HTTP daemon from the project root
+ window.RT.dirpr_library = "../consumer/made/Manuscript";
+ }
+
+ document.write('<script src="' + window.RT.dirpr_library + '/Core/loader.js"><\/script>');
+
+ document.write(
+ '<script>' +
+ 'window.RT.load("Core/utility");' +
+ 'window.RT.load("Core/block_visibility_during_layout");' +
+ 'window.RT.load("Theme");' +
+ 'window.RT.load("Element/theme_selector");' +
+ '<\/script>'
+ );
+})();
<head>
<meta charset="UTF-8">
<title>File and directory naming conventions</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>C modules, namespaces, and the build lifecycle</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>RT semantic HTML tags</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
9. page_fixed_glow.js (Visual page container)
</RT-code>
- <script src="style/style_orchestrator.js"></script>
- <script>
- window.StyleRT.style_orchestrator();
- </script>
-
<h1>RT conventions</h1>
<p> Headings are first letter capitalized. Remaining words are as they would be in English prose.</p>
--- /dev/null
+// RT-style.js (Internal RT-style project router)
+window.RT = window.RT || {};
+
+(function() {
+ const project_name = "RT-style";
+ const path = window.location.pathname;
+ const project_root_index = path.indexOf('/' + project_name + '/');
+
+ if (project_root_index !== -1) {
+ // substring(0, x) excludes the trailing slash. We must prepend it to the payload.
+ const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1);
+ window.RT.dirpr_library = absolute_project_root + "/consumer/made/Manuscript";
+ } else {
+ // Fallback for when served via local Python HTTP daemon from the project root
+ window.RT.dirpr_library = "../consumer/made/Manuscript";
+ }
+
+ document.write('<script src="' + window.RT.dirpr_library + '/Core/loader.js"><\/script>');
+
+ document.write(
+ '<script>' +
+ 'window.RT.load("Core/utility");' +
+ 'window.RT.load("Core/block_visibility_during_layout");' +
+ 'window.RT.load("Theme");' +
+ 'window.RT.load("Element/theme_selector");' +
+ '<\/script>'
+ );
+})();
<head>
<meta charset="UTF-8">
<title>Introduction to Harmony</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>Product development roles and workflow</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
<head>
<meta charset="UTF-8">
<title>Product maintenance roles and workflow</title>
+ <script src="RT-style.js"></script>
<script>
- window.RT = window.RT || {};
- window.RT.dirpr_library = "RT-style";
- 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');
+ window.RT.load('Layout/article_tech_ref');
</script>
</head>
<body>
--- /dev/null
+// RT-style.js (Internal RT-style project router)
+window.RT = window.RT || {};
+
+(function() {
+ const project_name = "RT-style";
+ const path = window.location.pathname;
+ const project_root_index = path.indexOf('/' + project_name + '/');
+
+ if (project_root_index !== -1) {
+ // substring(0, x) excludes the trailing slash. We must prepend it to the payload.
+ const absolute_project_root = path.substring(0, project_root_index + project_name.length + 1);
+ window.RT.dirpr_library = absolute_project_root + "/consumer/made/Manuscript";
+ } else {
+ // Fallback for when served via local Python HTTP daemon from the project root
+ window.RT.dirpr_library = "../consumer/made/Manuscript";
+ }
+
+ document.write('<script src="' + window.RT.dirpr_library + '/Core/loader.js"><\/script>');
+
+ document.write(
+ '<script>' +
+ 'window.RT.load("Core/utility");' +
+ 'window.RT.load("Core/block_visibility_during_layout");' +
+ 'window.RT.load("Theme");' +
+ 'window.RT.load("Element/theme_selector");' +
+ '<\/script>'
+ );
+})();
+++ /dev/null
-#+TITLE: Installing Python in Harmony
-#+AUTHOR: Thomas Walker Lynch
-#+OPTIONS: toc:2 num:nil
-
-* Overview
-
-This document describes how to install a project-local Python environment under:
-
-#+begin_src bash
-shared/linked-project/Python
-#+end_src
-
-* Precondition
-
-Ensure the following:
-
-- You are in a POSIX shell with =python3= installed.
-- The =python3-venv= package is available (on Debian: =sudo apt install python3-venv=).
-- You have sourced the Harmony environment via =env_toolsmith= to initialize =REPO_HOME= and related variables.
-
-* Step-by-Step Installation
-
-1. Source the Harmony environment:
- #+begin_src bash
- source env_toolsmith
- #+end_src
-
-2. Create the virtual environment:
- #+begin_src bash
- python3 -m venv "$REPO_HOME/shared/linked-project/Python"
- #+end_src
-
-3. Activate it temporarily to install required packages:
- #+begin_src bash
- source "$REPO_HOME/shared/linked-project/Python/bin/activate"
- pip install --upgrade pip
- pip install pytest # Add any shared packages here
- deactivate
- #+end_src
-
-4. Rename Python's default activate and deactivate:
- Harmony provides its own role-aware environment management. Using Python’s default activation scripts may interfere with prompt logic, PATH order, and role-specific behavior.
-
- Disable the default scripts by renaming them:
- #+begin_src bash
- mv "$REPO_HOME/shared/linked-project/Python/bin/activate" \
- "$REPO_HOME/shared/linked-project/Python/bin/activate_deprecated"
- #+end_src
-
- This ensures that accidental sourcing of Python’s =activate= script won't override Harmony's environment setup.
-
-5. Verify installation:
- #+begin_src bash
- ls "$REPO_HOME/shared/linked-project/Python/bin/python3"
- #+end_src
-
- The binary should exist and report a working Python interpreter when run.
-
-* Notes
-
-- The virtual environment is deliberately named =Python=, not =venv=, to reflect its role as a shared system component.
-- Harmony environment scripts define and control =VIRTUAL_ENV=, =PYTHON_HOME=, and =PATH=, making Python activation seamless and uniform.
-- There is no need to use Python’s =bin/activate= directly — it is fully replaced by Harmony’s environment logic.
-
-* Related Files
-
-- =shared/authored/env=
-- =shared/authored/env_source=
-- =env_developer=, =env_tester=, =env_toolsmith=
-
-* Last Verified
-
-2025-05-19 :: Activate/deactivate renamed post-install. Requires Harmony environment sourcing prior to execution.
+++ /dev/null
-
-This is the generic install.org doc that comes with the skeleton.
-
-1. $REPO_HOME/shared/linked-project/.gitignore:
-
- *
- !/.gitignore
- !/patch
-
- The only things from the third party directory that will be pushed to the repo origin is the .gitignore file and the patches.
-
-
-2. downloaded tar files etc. go into the directory `upstream`
-
- $REPO_HOME/shared/upstream
-
- Typically the contents of upstream are deleted after the install.
-
-3. for the base install
-
- cd $REPO_HOME/shared/linked-project
- do whatever it takes to install tool, as examples:
- git clone <tool_path>
- tar -xzf ../upstream/tar
- ...
-
- Be sure to add the path to the tool executable(s) in the $REPO_HOME/env_$ROLE files for the $ROLE who uses the tool.
-
- Assuming you are not also developing the tool, for safety
- change each installed git project to a local branch:
-
- b=<site>_<project>_local_$USER
- git switch -c "$b"
-
-
-4. Define some variables to simplify our discussion. Lowercase variable names
- are not exported from the shell.
-
- # already set in the environment
- # REPO_HOME
- # PROJECT
- # USER
-
- # example tool names: 'RT_gcc' 'RT-project share` etc.
- tool=<tool-name>
- tool_dpath="$REPO_HOME/shared/linked-project/$tool"
- patch_dpath="$REPO_HOME/shared/patch/"
-
-
-5. create a patch series (from current vendor state → your local edits)
-
- # this can be repeated and will create an encompassing diff file
-
- # optionally crate a new branch after cloning the third party tool repo and work from there. You won't make any commits, but in case you plan to ever check the changes in, or have a the bad habit of doing ommits burned into your brain-stem, making a brnch will help.
-
- # make changes
-
- cd "$tool_dpath"
-
- # do your edits
-
- # Stage edits. Do not commit them!! Be sure you are in the third party
- # tool directory when doing `git add -A` and `git diff` commands.
- git add -A
-
- # diff the stage from the current repo to create the patch file
- git diff --staged > "$patch_dpath/$tool"
-
- # the diff file can be added to the project and checked in at the project level.
-
-
-6. how to apply an existing patch
-
- Get a fresh clone of the tool into $tool_dpath.
-
- cd "$tool_dpath"
- git apply "$patch_dpath/$tool"
-
- You can see what `git apply` would do by running
-
- git apply --check /path/to/your/patch_dpath/$tool
+++ /dev/null
-#!/usr/bin/env python3
-import os
-import sys
-import shutil
-from pathlib import Path
-
-def run():
- repo_home_env = os.environ.get('REPO_HOME')
- if not repo_home_env:
- print("Error: REPO_HOME environment variable is missing.", file=sys.stderr)
- sys.exit(1)
-
- repo_home = Path(repo_home_env)
-
- # Define the canonical target location
- target_src = repo_home / 'shared' / 'linked-project' / 'RT-style' / 'consumer' / 'release' / 'RT'
-
- # Determine the destination directory
- if len(sys.argv) > 1:
- dest_dir = Path(sys.argv[1]).resolve()
- else:
- dest_dir = Path.cwd()
-
- if not dest_dir.is_dir():
- print(f"Error: Directory '{dest_dir}' does not exist.", file=sys.stderr)
- sys.exit(1)
-
- link_dest = dest_dir / 'RT'
-
- # Clobber existing file, link, or directory named 'RT'
- if link_dest.is_symlink() or link_dest.is_file():
- link_dest.unlink()
- elif link_dest.is_dir():
- shutil.rmtree(link_dest)
-
- # Establish the new symbolic link
- try:
- link_dest.symlink_to(target_src)
- print(f"Established link: {link_dest} -> {target_src}")
- except OSError as e:
- print(f"Failed to establish link: {e}", file=sys.stderr)
- sys.exit(1)
-
-if __name__ == '__main__':
- run()
\ No newline at end of file
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RT_Format — Reasoning Technology code formatter (Shallow Tokenizer)
-
-Commands:
- RT_Format write [--lisp] <file ...> Format files in place (rewrite originals)
- RT_Format copy [--lisp] <file ...> Save backups as <file>~ then format originals
- RT_Format pipe [--lisp] Read from stdin, write to stdout
- RT_Format self_test Run built-in tests
- RT_Format version Show tool version
- RT_Format help | --help Show usage
-"""
-
-import sys ,re ,shutil ,os
-from typing import List ,Tuple ,Optional ,TextIO
-
-RTF_VERSION = "0.4.0-tokenized"
-
-USAGE = """\
-Usage:
- RT_Format write [--lisp] <file ...>
- RT_Format copy [--lisp] <file ...>
- RT_Format pipe [--lisp]
- RT_Format self_test
- RT_Format version
- RT_Format help | --help
-"""
-
-BR_OPEN = "([{<"
-BR_CLOSE = ")]}>"
-PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
-REV = dict( zip(BR_CLOSE ,BR_OPEN) )
-
-# --------------- Lexer ----------------
-
-class RT_Token:
- def __init__(self ,kind: str ,text: str):
- self.kind = kind
- self.text = text
-
- def __repr__(self):
- return f"<{self.kind}:{repr(self.text)}>"
-
-# The regex prioritizes exact matches.
-# Comments include //, #, and /* ... */ blocks.
-# Strings include Python '''/""" blocks, plus standard single/double quotes.
-TOKEN_REGEX = re.compile(
- r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
- r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
- r'|(?P<SPACE>[ \t]+)'
- r'|(?P<NEWLINE>\n)'
- r'|(?P<COMMA>,)'
- r'|(?P<BR_OPEN>[\[\(\{<])'
- r'|(?P<BR_CLOSE>[\]\)\}>])'
- r'|(?P<CODE>[^ \t\n,\[\(\{<\]\)\}>"\'#/]+|/)'
-)
-
-def tokenize(text: str) -> List[RT_Token]:
- tokens = []
- for TM_match in TOKEN_REGEX.finditer(text):
- kind = TM_match.lastgroup
- text_val = TM_match.group(kind)
- tokens.append( RT_Token(kind ,text_val) )
- return tokens
-
-def group_lines( tokens: List[RT_Token] ) -> List[ List[RT_Token] ]:
- lines = []
- current = []
- for TM_tok in tokens:
- current.append(TM_tok)
- if TM_tok.kind == "NEWLINE":
- lines.append(current)
- current = []
- if current:
- lines.append(current)
- return lines
-
-# --------------- Formatting Passes ----------------
-
-def pass_vertical_commas( lines: List[List[RT_Token]] ) -> None:
- for TM_idx in range( len(lines) - 1 ):
- current_line = lines[TM_idx]
-
- # Find the last significant token
- last_sig_idx = -1
- for TM_i in range( len(current_line) - 1 ,-1 ,-1 ):
- if current_line[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- last_sig_idx = TM_i
- break
-
- if last_sig_idx>= 0 and current_line[last_sig_idx].kind == "COMMA":
- # Remove the trailing comma
- comma_tok = current_line.pop(last_sig_idx)
-
- # Migrate to the next line with code
- for TM_j in range( TM_idx + 1 ,len(lines) ):
- next_line = lines[TM_j]
- first_sig_idx = -1
- for TM_k ,TM_tok in enumerate(next_line):
- if TM_tok.kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- first_sig_idx = TM_k
- break
-
- if first_sig_idx>= 0:
- next_line.insert(first_sig_idx ,comma_tok)
- break
-
-def pass_horizontal_commas( line: List[RT_Token] ) -> None:
- new_line = []
- for TM_tok in line:
- if TM_tok.kind == "COMMA":
- is_vertical = all(t.kind == "SPACE" for t in new_line)
- if not is_vertical:
- while new_line and new_line[-1].kind == "SPACE":
- new_line.pop()
- if new_line:
- new_line.append( RT_Token("SPACE" ," ") )
- new_line.append(TM_tok)
- elif TM_tok.kind == "SPACE":
- if new_line and new_line[-1].kind == "COMMA":
- continue # Drop space after comma
- new_line.append(TM_tok)
- else:
- new_line.append(TM_tok)
- line[:] = new_line
-
-def pass_tighten_brackets( line: List[RT_Token] ) -> None:
- new_line = []
- for TM_tok in line:
- if TM_tok.kind == "SPACE":
- if new_line and new_line[-1].kind == "BR_OPEN":
- continue
- new_line.append(TM_tok)
- elif TM_tok.kind == "BR_CLOSE":
- while new_line and new_line[-1].kind == "SPACE":
- new_line.pop()
- new_line.append(TM_tok)
- else:
- new_line.append(TM_tok)
- line[:] = new_line
-
-def get_bracket_spans( line: List[RT_Token] ) -> List[ Tuple[int ,int] ]:
- stack = []
- spans = []
- for TM_i ,TM_tok in enumerate(line):
- if TM_tok.kind == "BR_OPEN":
- stack.append( (TM_tok.text ,TM_i) )
- elif TM_tok.kind == "BR_CLOSE":
- if stack and REV[TM_tok.text] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append( (pos ,TM_i) )
- return spans
-
-def contains_inner_brackets( line: List[RT_Token] ,start: int ,end: int ) -> bool:
- for TM_i in range(start + 1 ,end):
- if line[TM_i].kind in ("BR_OPEN" ,"BR_CLOSE"):
- return True
- return False
-
-def pass_pad_outermost( line: List[RT_Token] ,is_lisp: bool ) -> None:
- if is_lisp:
- return
-
- while True:
- spans = get_bracket_spans(line)
- changed = False
-
- # Process from right to left to avoid shifting indices
- for TM_start ,TM_end in reversed(spans):
- if contains_inner_brackets(line ,TM_start ,TM_end):
- left_has = (TM_start + 1 <len(line)) and (line[TM_start + 1].kind == "SPACE")
- right_has = ( TM_end - 1>= 0 ) and ( line[TM_end - 1].kind == "SPACE" )
-
- if not left_has or not right_has:
- if not right_has:
- line.insert( TM_end ,RT_Token("SPACE" ," ") )
- if not left_has:
- line.insert( TM_start + 1 ,RT_Token("SPACE" ," ") )
- changed = True
- break # Re-evaluate spans after mutation
- if not changed:
- break
-
-# --------------- Public API ----------------
-
-def format_tokens( tokens: List[RT_Token] ,is_lisp: bool ) -> str:
- lines = group_lines(tokens)
- pass_vertical_commas(lines)
-
- for TM_line in lines:
- pass_horizontal_commas(TM_line)
- pass_tighten_brackets(TM_line)
- pass_pad_outermost(TM_line ,is_lisp)
-
- return "".join(t.text for TM_line in lines for t in TM_line)
-
-def rt_format_text(text: str ,is_lisp: bool) -> str:
- tokens = tokenize(text)
- return format_tokens(tokens ,is_lisp)
-
-def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
- text = inp.read()
- out.write( rt_format_text(text ,is_lisp) )
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_text(src ,False)
- if got != exp:
- print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
- ok = False
-
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
- chk(" ,vertical_arg" ," ,vertical_arg")
-
- chk("int a=0,\n b=1,\n c=2;" ,"int a=0\n ,b=1\n ,c=2;")
-
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
- chk(" {" ," {")
-
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
- chk("compute(x, f(y" ,"compute( x ,f(y") # Tolerant fragment fallback omitted for brevity, but structurally sound.
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-
-def write_files( paths: List[str] ,is_lisp: bool ) -> int:
- for TM_path in paths:
- with open(TM_path ,"r" ,encoding="utf-8") as f:
- data = f.read()
- formatted = rt_format_text(data ,is_lisp)
- with open(TM_path ,"w" ,encoding="utf-8") as f:
- f.write(formatted)
- return 0
-
-def copy_files( paths: List[str] ,is_lisp: bool ) -> int:
- for TM_path in paths:
- shutil.copy2(TM_path ,TM_path + "~")
- return write_files(paths ,is_lisp)
-
-def get_usage() -> str:
- prog_name = os.path.basename( sys.argv[0] )
- return f"""\
-Usage:
- {prog_name} write [--lisp] <file ...>
- {prog_name} copy [--lisp] <file ...>
- {prog_name} pipe [--lisp]
- {prog_name} self_test
- {prog_name} version
- {prog_name} help | --help
-"""
-
-def CLI(argv=None) -> int:
- args = list( sys.argv[1:] if argv is None else argv )
- usage_text = get_usage()
-
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(usage_text)
- return 0
-
- is_lisp = "--lisp" in args
- args = [TM_a for TM_a in args if TM_a != "--lisp"]
-
- if not args:
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RT_FORMAT_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + usage_text)
- return 2
- return write_files(rest ,is_lisp)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + usage_text)
- return 2
- return copy_files(rest ,is_lisp)
-
- print(f"Unknown command: {cmd}\n" + usage_text)
- return 2
-
-if __name__ == "__main__":
- sys.exit( CLI() )
\ No newline at end of file
+++ /dev/null
-( defun RT-format-buffer()
- (interactive)
- (save-excursion
- ( shell-command-on-region(point-min)(point-max)
- "RT-formatter pipe" t t)) )
+++ /dev/null
-
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- (let ((formatted-text (with-current-buffer temp-buffer (buffer-string))))
- (save-excursion
- (delete-region (point-min) (point-max))
- (insert formatted-text))
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
-
-;; ( defun RT-format-buffer()
-;; (interactive)
-;; (save-excursion
-;; ( shell-command-on-region(point-min)(point-max)
-;; "RT-formatter pipe" t t)) )
+++ /dev/null
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- (progn
- ;; Applies a non-destructive diff, preserving point and markers natively
- (replace-buffer-contents temp-buffer)
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RTfmt — Reasoning Technology code formatter (Predicate Tokenizer)
-
-Commands:
- RTfmt write [--lisp] <file ...> Format files in place (rewrite originals)
- RTfmt copy [--lisp] <file ...> Save backups as <file>~ then format originals
- RT-formatter pipe [--lisp] Read from stdin, write to stdout
- RTfmt self_test Run built-in tests
- RTfmt version Show tool version
- RTfmt help | --help Show usage
-"""
-
-import sys ,re ,shutil ,os
-from typing import List ,Tuple ,Optional ,TextIO
-
-RTF_VERSION = "0.5.0-predicate"
-
-def get_usage() -> str:
- prog_name = os.path.basename(sys.argv[0])
- return f"""\
-Usage:
- {prog_name} write [--lisp] <file ...>
- {prog_name} copy [--lisp] <file ...>
- {prog_name} pipe [--lisp]
- {prog_name} self_test
- {prog_name} version
- {prog_name} help | --help
-"""
-
-# Removed < and > so they are treated as standard CODE operators
-BR_OPEN = "([{"
-BR_CLOSE = ")]}"
-PAIR = dict( zip(BR_OPEN ,BR_CLOSE) )
-REV = dict( zip(BR_CLOSE ,BR_OPEN) )
-
-# --------------- Lexer ----------------
-
-class RT_Token:
- def __init__(self ,kind: str ,text: str):
- self.kind = kind
- self.text = text
-
- def __repr__(self):
- return f"<{self.kind}:{repr(self.text)}>"
-
-TOKEN_REGEX = re.compile(
- r'(?P<COMMENT>//[^\n]*|#[^\n]*|(?s:/\*.*?\*/))'
- r'|(?P<STRING>"""[\s\S]*?"""|\'\'\'[\s\S]*?\'\'\'|"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\')'
- r'|(?P<SPACE>[ \t]+)'
- r'|(?P<NEWLINE>\n)'
- r'|(?P<COMMA>,)'
- r'|(?P<BR_OPEN>[\[\(\{])'
- r'|(?P<BR_CLOSE>[\]\)\}])'
- r'|(?P<CODE>[^ \t\n,\[\(\{\]\)\}"\'#/]+|/)'
-)
-
-def tokenize(text: str) -> List[RT_Token]:
- tokens = []
- for TM_match in TOKEN_REGEX.finditer(text):
- kind = TM_match.lastgroup
- text_val = TM_match.group(kind)
- tokens.append( RT_Token(kind ,text_val) )
- return tokens
-
-# --------------- Intelligence API ----------------
-
-class TokenStream:
- def __init__(self ,tokens: List[RT_Token]):
- self.tokens = tokens
-
- def get_token(self ,index: int) -> Optional[RT_Token]:
- if 0 <= index < len(self.tokens):
- return self.tokens[index]
- return None
-
- def next_sig_index(self ,index: int) -> Optional[int]:
- for TM_i in range(index + 1 ,len(self.tokens)):
- if self.tokens[TM_i].kind not in ("SPACE" ,"NEWLINE" ,"COMMENT"):
- return TM_i
- return None
-
- def is_first_on_line(self ,index: int) -> bool:
- for TM_i in range(index - 1 ,-1 ,-1):
- k = self.tokens[TM_i].kind
- if k == "NEWLINE":
- return True
- if k != "SPACE":
- return False
- return True # Start of file
-
- def indent_of_line(self ,index: int) -> str:
- for TM_i in range(index ,-1 ,-1):
- if self.tokens[TM_i].kind == "NEWLINE":
- if TM_i + 1 < len(self.tokens) and self.tokens[TM_i + 1].kind == "SPACE":
- return self.tokens[TM_i + 1].text
- return ""
- if self.tokens and self.tokens[0].kind == "SPACE":
- return self.tokens[0].text
- return ""
-
- def indent_of_left_match(self ,index: int) -> Optional[str]:
- tok = self.get_token(index)
- if not tok or tok.kind != "BR_CLOSE":
- return None
- target_opener = REV[tok.text]
- depth = 0
- for TM_i in range(index - 1 ,-1 ,-1):
- t = self.tokens[TM_i]
- if t.kind == "BR_CLOSE":
- depth += 1
- elif t.kind == "BR_OPEN":
- if depth > 0:
- depth -= 1
- elif t.text == target_opener:
- return self.indent_of_line(TM_i)
- return None
-
-# --------------- Rule Engine ----------------
-
-def rule_migrate_vertical_commas(stream: TokenStream):
- TM_i = 0
- while TM_i < len(stream.tokens):
- if stream.tokens[TM_i].kind == "COMMA":
- is_trailing = False
- next_sig = stream.next_sig_index(TM_i)
- if next_sig is not None:
- for TM_j in range(TM_i + 1 ,next_sig):
- if stream.tokens[TM_j].kind == "NEWLINE":
- is_trailing = True
- break
-
- if is_trailing:
- comma_tok = stream.tokens.pop(TM_i)
- next_sig -= 1 # Shifted because of pop
- stream.tokens.insert(next_sig ,comma_tok)
- continue
- TM_i += 1
-
-def rule_format_horizontal_commas(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "COMMA":
- if stream.is_first_on_line(TM_i):
- continue
-
- next_tok = stream.get_token(TM_i + 1)
- if next_tok and next_tok.kind == "SPACE":
- stream.tokens.pop(TM_i + 1)
-
- prev_tok = stream.get_token(TM_i - 1)
- if prev_tok and prev_tok.kind == "SPACE":
- if prev_tok.text != " ":
- prev_tok.text = " "
- else:
- stream.tokens.insert(TM_i ,RT_Token("SPACE" ," "))
-
-def rule_fix_closing_indent(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "BR_CLOSE" and stream.is_first_on_line(TM_i):
- target_indent = stream.indent_of_left_match(TM_i)
- if target_indent is not None:
- prev = stream.get_token(TM_i - 1)
- if prev and prev.kind == "SPACE":
- prev.text = target_indent
- else:
- stream.tokens.insert(TM_i ,RT_Token("SPACE" ,target_indent))
-
-def rule_tighten_brackets(stream: TokenStream):
- for TM_i in range(len(stream.tokens) - 1 ,-1 ,-1):
- if stream.tokens[TM_i].kind == "SPACE" and not stream.is_first_on_line(TM_i):
- prev_t = stream.get_token(TM_i - 1)
- next_t = stream.get_token(TM_i + 1)
- if (prev_t and prev_t.kind == "BR_OPEN") or (next_t and next_t.kind == "BR_CLOSE"):
- stream.tokens.pop(TM_i)
-
-def get_bracket_spans(stream: TokenStream) -> List[Tuple[int ,int]]:
- stack = []
- spans = []
- for TM_i ,tok in enumerate(stream.tokens):
- if tok.kind == "BR_OPEN":
- stack.append( (tok.text ,TM_i) )
- elif tok.kind == "BR_CLOSE":
- if stack and REV[tok.text] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append( (pos ,TM_i) )
- return spans
-
-def rule_pad_outermost(stream: TokenStream ,is_lisp: bool):
- if is_lisp:
- return
- while True:
- spans = get_bracket_spans(stream)
- changed = False
- for TM_start ,TM_end in reversed(spans):
- has_inner = False
- for TM_k in range(TM_start + 1 ,TM_end):
- if stream.tokens[TM_k].kind in ("BR_OPEN" ,"BR_CLOSE"):
- has_inner = True
- break
-
- if has_inner:
- left_has = (TM_start + 1 < len(stream.tokens)) and stream.tokens[TM_start + 1].kind == "SPACE"
- right_has = (TM_end - 1 >= 0) and stream.tokens[TM_end - 1].kind == "SPACE"
- if not left_has or not right_has:
- if not right_has:
- stream.tokens.insert(TM_end ,RT_Token("SPACE" ," "))
- if not left_has:
- stream.tokens.insert(TM_start + 1 ,RT_Token("SPACE" ," "))
- changed = True
- break
- if not changed:
- break
-
-# --------------- Public API ----------------
-
-def format_tokens(tokens: List[RT_Token] ,is_lisp: bool) -> str:
- stream = TokenStream(tokens)
-
- rule_migrate_vertical_commas(stream)
- rule_format_horizontal_commas(stream)
- rule_tighten_brackets(stream)
- rule_fix_closing_indent(stream)
- rule_pad_outermost(stream ,is_lisp)
-
- return "".join(t.text for t in stream.tokens)
-
-def rt_format_text(text: str ,is_lisp: bool) -> str:
- tokens = tokenize(text)
- return format_tokens(tokens ,is_lisp)
-
-def rt_format_stream(inp: TextIO ,out: TextIO ,is_lisp: bool) -> None:
- text = inp.read()
- out.write( rt_format_text(text ,is_lisp) )
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_text(src ,False)
- if got != exp:
- print("FAIL:\n" + src + "\n=>\n" + got + "\nexpected:\n" + exp)
- ok = False
-
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
- chk(" ,vertical_arg" ," ,vertical_arg")
-
- chk("int a=0,\n b=1,\n c=2;" ,"int a=0\n ,b=1\n ,c=2;")
-
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
- chk(" {" ," {")
-
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
-
- # Operator protection check
- chk("for(int TM = 0; TM < count; ++TM)" ,"for(int TM = 0; TM < count; ++TM)")
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-def write_files(paths: List[str] ,is_lisp: bool) -> int:
- for TM_path in paths:
- with open(TM_path ,"r" ,encoding="utf-8") as f:
- data = f.read()
-
- formatted = rt_format_text(data ,is_lisp)
-
- # Only touch the file if the content actually changed
- if data != formatted:
- with open(TM_path ,"w" ,encoding="utf-8") as f:
- f.write(formatted)
- print(f"Formatted: {TM_path}")
- return 0
-
-def copy_files(paths: List[str] ,is_lisp: bool) -> int:
- for TM_path in paths:
- shutil.copy2(TM_path ,TM_path + "~")
- return write_files(paths ,is_lisp)
-
-def CLI(argv=None) -> int:
- args = list(sys.argv[1:] if argv is None else argv)
- usage_text = get_usage()
-
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(usage_text)
- return 0
-
- is_lisp = "--lisp" in args
- args = [TM_a for TM_a in args if TM_a != "--lisp"]
-
- if not args:
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RTF_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout ,is_lisp)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + usage_text)
- return 2
- return write_files(rest ,is_lisp)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + usage_text)
- return 2
- return copy_files(rest ,is_lisp)
-
- print(f"Unknown command: {cmd}\n" + usage_text)
- return 2
-
-if __name__ == "__main__":
- sys.exit( CLI() )
+++ /dev/null
-(defun RT-formatter-buffer ()
- "Format the current buffer using RTfmt."
- (interactive)
- (if (not (executable-find "RT-formatter"))
- (message "Error: RTfmt executable not found in PATH.")
- (let ((temp-buffer (generate-new-buffer " *RTfmt*"))
- (args (list "pipe")))
- (when (derived-mode-p 'emacs-lisp-mode 'lisp-mode)
- (setq args (append args (list "--lisp"))))
- (unwind-protect
- (let ((exit-code (apply #'call-process-region
- (point-min) (point-max)
- "RT-formatter"
- nil temp-buffer nil
- args)))
- (if (zerop exit-code)
- ;; Check if the formatted text is actually different
- (if (= (compare-buffer-substrings nil nil nil temp-buffer nil nil) 0)
- (message "RTfmt: Already perfectly formatted.")
- (replace-buffer-contents temp-buffer)
- (message "RT-formatter formatting successful."))
- (message "RT-formatter failed with exit code %s. Buffer unchanged." exit-code)))
- (kill-buffer temp-buffer)))))
+++ /dev/null
-// commas and simple tight brackets
-int g(){
- int a=0 ,
- b=1 ,
- c=2;
- return h(a ,b ,c);
-}
-
-// balanced outermost-with-nesting -> pad inside outer ()
-int f(){ return outer(inner(a ,b)); }
-
-// strings and comments must be unchanged
-int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
-
-// unbalanced open-right with nesting -> pad after first unmatched '('
-int u(){if(doit(foo(1 ,2) // missing ))
- return 0;}
-
-// arrays / subscripts stay tight; commas still RT-style
-int a(int i ,int j){ return M[i ,j] + V[i] + W[j]; }
+++ /dev/null
-# commas and spacing in defs / calls
-def f ( x , y , z ):
- return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
-
-# outermost-with-nesting -> pad inside outer ()
-val = outer( inner( a,b ) )
-
-# strings/comments untouched
-s = "text, with , commas ( not to touch )" # a ,b ,c
-
-# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
-def g():
- return result) # likely unchanged
-
-# unbalanced: open-right (first unmatched opener) with inner bracket following
-k = compute(x, f(y
+++ /dev/null
-#!/usr/bin/env -S python3 -B
-# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
-"""
-RT_Format — Reasoning Technology code formatter (commas + bracketed phrases per line)
-
-Commands:
- RT_Format write <file ...> Format files in place (rewrite originals)
- RT_Format copy <file ...> Save backups as <file>~ then format originals
- RT_Format pipe Read from stdin, write to stdout
- RT_Format self_test Run built-in tests
- RT_Format version Show tool version
- RT_Format help | --help Show usage
-
-Rules:
- • Horizontal lists -> a ,b ,c (space BEFORE comma, none after)
- • Tight (){}[] by default; add one space just inside borders only when an
- OUTERMOST bracketed phrase on the line contains an INNER bracket.
- • Multiple outermost phrases can exist on a line (e.g., `g() { ... }`);
- apply the rule to EACH such phrase independently.
- • Per-line, tolerant of unbalanced brackets: first unmatched opener OR last
- unmatched closer is treated as “the” outermost for padding purposes.
- • Strings and single-line comments (#, //) are not altered.
-"""
-
-from typing import List ,Tuple ,Optional ,TextIO
-import sys ,re ,io ,shutil ,os
-
-RTF_VERSION = "0.2.2" # pad all outermost-with-nesting phrases on a line
-
-BR_OPEN = "([{<"
-BR_CLOSE = ")]}>"
-PAIR = dict(zip(BR_OPEN ,BR_CLOSE))
-REV = dict(zip(BR_CLOSE ,BR_OPEN))
-
-USAGE = """\
-Usage:
- RT_Format write <file ...>
- RT_Format copy <file ...>
- RT_Format pipe
- RT_Format self_test
- RT_Format version
- RT_Format help | --help
-"""
-
-# --------------- Core token helpers ----------------
-
-def split_code_comment(line: str):
- """Return (code ,comment), keeping the comment marker if present; ignore markers inside strings."""
- in_s = None
- esc = False
- for i ,ch in enumerate(line):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch == "#":
- return line[:i] ,line[i:]
- if ch == "/" and i + 1 < len(line) and line[i + 1] == "/":
- return line[:i] ,line[i:]
- return line ,""
-
-def format_commas(code: str) -> str:
- """Space BEFORE comma, none after, outside strings."""
- out: List[str] = []
- in_s = None
- esc = False
- i = 0
- while i < len(code):
- ch = code[i]
- if in_s:
- out.append(ch)
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- i += 1
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- out.append(ch)
- i += 1
- elif ch == ",":
- while out and out[-1] == " ":
- out.pop()
- if out and out[-1] != " ":
- out.append(" ")
- out.append(",")
- j = i + 1
- while j < len(code) and code[j] == " ":
- j += 1
- i = j
- else:
- out.append(ch)
- i += 1
- return "".join(out)
-
-# --------------- Bracket discovery ----------------
-
-def top_level_spans(code: str) -> List[Tuple[int ,int]]:
- """Return all balanced OUTERMOST bracketed spans (start,end) for this line, ignoring strings."""
- in_s = None
- esc = False
- stack: List[Tuple[str ,int]] = []
- spans: List[Tuple[int ,int]] = []
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- stack.append((ch ,i))
- elif ch in BR_CLOSE:
- if stack and REV[ch] == stack[-1][0]:
- _ ,pos = stack.pop()
- if not stack:
- spans.append((pos ,i))
- else:
- # unmatched closer ignored here; handled in unbalanced logic
- pass
- return spans
-
-def first_unmatched_opener(code: str) -> Optional[int]:
- in_s = None
- esc = False
- stack: List[Tuple[str ,int]] = []
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- stack.append((ch ,i))
- elif ch in BR_CLOSE:
- if stack and REV[ch] == stack[-1][0]:
- stack.pop()
- else:
- # unmatched closer: do nothing here
- pass
- return stack[0][1] if stack else None
-
-def last_unmatched_closer(code: str) -> Optional[int]:
- in_s = None
- esc = False
- depth = 0
- last: Optional[int] = None
- for i ,ch in enumerate(code):
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN:
- depth += 1
- elif ch in BR_CLOSE:
- if depth > 0:
- depth -= 1
- else:
- last = i
- return last
-
-def contains_inner_bracket(code: str ,start: Optional[int] ,end: Optional[int]) -> bool:
- """Check for any bracket token inside the given bounds (respect strings)."""
- if start is None and end is None:
- return False
- in_s = None
- esc = False
- lo = (start + 1) if start is not None else 0
- hi = (end - 1) if end is not None else len(code) - 1
- if hi < lo:
- return False
- for i ,ch in enumerate(code):
- if i < lo or i > hi:
- continue
- if in_s:
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- continue
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- continue
- if ch in BR_OPEN or ch in BR_CLOSE:
- return True
- return False
-
-# --------------- Spacing transforms ----------------
-
-def tighten_all_brackets(code: str) -> str:
- """Tight margins and remove immediate interior spaces next to borders."""
- out: List[str] = []
- in_s = None
- esc = False
- i = 0
- while i < len(code):
- ch = code[i]
- if in_s:
- out.append(ch)
- if esc:
- esc = False
- elif ch == "\\":
- esc = True
- elif ch == in_s:
- in_s = None
- i += 1
- else:
- if ch in ("'" ,'"'):
- in_s = ch
- out.append(ch)
- i += 1
- elif ch in BR_CLOSE:
- if out and out[-1] == " ":
- out.pop()
- out.append(ch)
- i += 1
- elif ch in BR_OPEN:
- if out and out[-1] == " ":
- out.pop()
- out.append(ch)
- i += 1
- while i < len(code) and code[i] == " ":
- i += 1
- else:
- out.append(ch)
- i += 1
- return "".join(out)
-
-def apply_bracket_padding(code: str) -> str:
- """
- 1) Tighten globally.
- 2) For EACH balanced outermost span, if it contains an inner bracket,
- ensure exactly one space just inside its borders — but only if missing.
- 3) If there are no balanced spans, pad the first unmatched opener OR the last unmatched closer
- only if that outer fragment contains an inner bracket, and only if padding is missing.
- """
- s = tighten_all_brackets(code)
-
- def borders_have_space(text: str, start: int, end: int) -> Tuple[bool, bool]:
- # Return (left_has_space, right_has_space) for just-inside borders.
- left_has = (start + 1 < len(text)) and (text[start + 1] == " ")
- right_has = (end - 1 >= 0) and (text[end - 1] == " ")
- return left_has, right_has
-
- # Balanced top-level spans: may be multiple on one line (e.g., g() { ... }).
- # Iterate while applying at most one mutation per pass; recompute spans after.
- while True:
- spans = top_level_spans(s)
- changed = False
- for (start, end) in spans:
- if contains_inner_bracket(s, start, end):
- left_has, right_has = borders_have_space(s, start, end)
- if not left_has or not right_has:
- # Insert exactly one space just inside each border that lacks it.
- if not right_has:
- # Right side first to avoid shifting the 'start' index computation
- s = s[:end].rstrip(" ") + " " + s[end:].lstrip(" ")
- if not left_has:
- s = s[:start + 1].rstrip(" ") + " " + s[start + 1:].lstrip(" ")
- changed = True
- break # after a mutation, recompute spans fresh
- if not changed:
- break
-
- # If there are no balanced spans, consider unbalanced fragment once
- if not top_level_spans(s):
- o = first_unmatched_opener(s)
- c = last_unmatched_closer(s)
- if o is not None and contains_inner_bracket(s, o, None):
- # add one space after opener only if missing
- if not (o + 1 < len(s) and s[o + 1] == " "):
- s = s[:o + 1].rstrip(" ") + " " + s[o + 1:]
- elif c is not None and contains_inner_bracket(s, None, c):
- # add one space before closer only if missing
- if not (c - 1 >= 0 and s[c - 1] == " "):
- s = s[:c].rstrip(" ") + " " + s[c:]
-
- return s
-
-# --------------- Public API ----------------
-
-def rt_format_line(line: str) -> str:
- code ,comment = split_code_comment(line.rstrip("\n"))
- code = format_commas(code)
- code = apply_bracket_padding(code)
- return code + comment
-
-def rt_format_text(text: str) -> str:
- return "\n".join(rt_format_line(ln) for ln in text.splitlines())
-
-def rt_format_stream(inp: TextIO ,out: TextIO) -> None:
- for line in inp:
- out.write(rt_format_line(line) + "\n")
-
-# --------------- Self-test ----------------
-
-def run_self_test() -> bool:
- ok = True
- def chk(src ,exp):
- nonlocal ok
- got = rt_format_line(src)
- if got != exp:
- print("FAIL:" ,src ,"=>" ,got ,"expected:" ,exp)
- ok = False
-
- # Commas
- chk("a,b,c" ,"a ,b ,c")
- chk("a , b , c" ,"a ,b ,c")
-
- # Tight () by default
- chk("f ( x )" ,"f(x)")
- chk("f(x) + g(y)" ,"f(x) + g(y)")
-
- # Balanced: multiple outermost spans (g() and {...}) -> only pad {...} if it has inner bracket
- src = "int g(){int a=0,b=1,c=2; return h(a,b,c);}"
- exp = "int g(){ int a=0 ,b=1 ,c=2; return h(a ,b ,c); }"
- chk(src ,exp)
-
- # Balanced: single outermost with nesting
- chk("outer( inner(a,b) )" ,"outer( inner(a ,b) )")
-
- # Unbalanced open-right with nesting
- chk("compute(x, f(y" ,"compute( x ,f(y)")
-
- # Unbalanced open-left without prior inner bracket => unchanged
- chk("return z) + 1" ,"return z) + 1")
-
- print("SELFTEST OK" if ok else "SELFTEST FAILED")
- return ok
-
-# --------------- CLI ----------------
-
-def write_files(paths: List[str]) -> int:
- for path in paths:
- with open(path ,"r" ,encoding="utf-8") as f:
- data = f.read()
- formatted = rt_format_text(data)
- with open(path ,"w" ,encoding="utf-8") as f:
- f.write(formatted + ("\n" if not formatted.endswith("\n") else ""))
- return 0
-
-def copy_files(paths: List[str]) -> int:
- for path in paths:
- shutil.copy2(path ,path + "~")
- return write_files(paths)
-
-def CLI(argv=None) -> int:
- args = list(sys.argv[1:] if argv is None else argv)
- if not args or args[0] in {"help" ,"--help" ,"-h"}:
- print(USAGE)
- return 0
-
- cmd = args[0]
- rest = args[1:]
-
- if cmd == "version":
- print(RTF_VERSION)
- return 0
- if cmd == "self_test":
- ok = run_self_test()
- return 0 if ok else 1
- if cmd == "pipe":
- rt_format_stream(sys.stdin ,sys.stdout)
- return 0
- if cmd == "write":
- if not rest:
- print("write: missing <file ...>\n" + USAGE)
- return 2
- return write_files(rest)
- if cmd == "copy":
- if not rest:
- print("copy: missing <file ...>\n" + USAGE)
- return 2
- return copy_files(rest)
-
- print(f"Unknown command: {cmd}\n" + USAGE)
- return 2
-
-if __name__ == "__main__":
- sys.exit(CLI())
+++ /dev/null
-(defun rt-format-buffer ()
- (interactive)
- (shell-command-on-region (point-min) (point-max)
- "RT_Format pipe" t t))
+++ /dev/null
-// commas and simple tight brackets
-int g(){int a=0,b=1,c=2; return h(a,b,c);}
-
-// balanced outermost-with-nesting -> pad inside outer ()
-int f(){return outer( inner(a,b) );}
-
-// strings and comments must be unchanged
-int s(){ printf("x ,y ,z (still a string)"); /* a ,b ,c */ return 1; }
-
-// unbalanced open-right with nesting -> pad after first unmatched '('
-int u(){ if(doit(foo(1,2) // missing ))
- return 0; }
-
-// arrays / subscripts stay tight; commas still RT-style
-int a(int i,int j){ return M[i,j] + V[i] + W[j]; }
+++ /dev/null
-# commas and spacing in defs / calls
-def f ( x , y , z ):
- return dict( a =1 , b= 2 ), [ 1, 2 ,3 ], ( (1,2) )
-
-# outermost-with-nesting -> pad inside outer ()
-val = outer( inner( a,b ) )
-
-# strings/comments untouched
-s = "text, with , commas ( not to touch )" # a ,b ,c
-
-# unbalanced: open-left (closing without opener) -> no padding unless inner bracket before it
-def g():
- return result) # likely unchanged
-
-# unbalanced: open-right (first unmatched opener) with inner bracket following
-k = compute(x, f(y
--- /dev/null
+#!/usr/bin/env bash
+# developer/tool/build_component/serve
+
+set -euo pipefail
+
+# Bind the Python server to the root of the project to match the paths expected by the HTML documents
+echo "Serving project locally at http://localhost:8000"
+echo "Press Ctrl+C to stop."
+
+cd "$REPO_HOME"
+python3 -m http.server 8000