--- /dev/null
+/*
+ Targets the root element to ensure total blackout during load.
+*/
+function body_visibility_hidden(){
+ const gate = document.createElement('style');
+ gate.id = 'rt-visibility-gate';
+ gate.textContent = 'html{visibility:hidden !important; background:black !important;}';
+ document.head.appendChild(gate);
+}
+
+window.StyleRT = window.StyleRT || {};
+window.StyleRT.body_visibility_hidden = body_visibility_hidden;
--- /dev/null
+/*
+ Restores visibility by removing the visibility gate.
+*/
+function body_visibility_visible(){
+ const gate = document.getElementById('rt-visibility-gate');
+ if (gate){
+ gate.remove();
+ }
+ document.body.style.visibility = 'visible';
+}
+
+window.StyleRT = window.StyleRT || {};
+window.StyleRT.body_visibility_visible = body_visibility_visible;
--- /dev/null
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.include = function(path_identifier){
+ let parts_seq = path_identifier.split('/');
+ let namespace = parts_seq[0];
+
+ let module_path = parts_seq.slice(1).join('/');
+
+ if(module_path === 'theme'){
+ let saved_theme = localStorage.getItem('RT_theme_preference');
+ if(!saved_theme){
+ saved_theme = 'dark_gold';
+ localStorage.setItem('RT_theme_preference' ,saved_theme);
+ }
+ module_path = 'theme/' + saved_theme;
+ }
+
+ let base_path = window.StyleRT_namespaces[namespace];
+ if(!base_path){
+ console.error("Namespace not found: " + namespace);
+ return;
+ }
+
+ let full_path = base_path + '/' + module_path + '.js';
+ // FIXED: No backslashes in the closing script tag
+ document.write('<script src="' + full_path + '"></script>');
+};
--- /dev/null
+/*
+ General utilities for the StyleRT library.
+*/
+
+window.StyleRT = window.StyleRT || {};
+
+// --- DEBUG SYSTEM ---
+window.StyleRT.debug = {
+
+ // all debug messages enabled
+/*
+ active_tokens: new Set([
+ 'style', 'layout', 'pagination'
+ ,'selector', 'config', 'error'
+ ,'term'
+ ]),
+*/
+ active_tokens: new Set([
+ 'term'
+ ]),
+
+ log: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.log(`[StyleRT:${token}]`, message);
+ }
+ },
+
+ warn: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.warn(`[StyleRT:${token}]`, message);
+ }
+ },
+
+ // New: Always log errors regardless of token, but tag them
+ error: function(token, message) {
+ console.error(`[StyleRT:${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.StyleRT.utility = {
+ // --- FONT PHYSICS ---
+ measure_ink_ratio: function(target_font, ref_font = null) {
+ const debug = window.StyleRT.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;
+ // debug.log('layout', `Ink Ratio calculated: ${ratio.toFixed(3)}`);
+
+ return {
+ ratio: ratio,
+ baseline_diff: ref_m.descent - target_m.descent
+ };
+ },
+
+ // --- COLOR PHYSICS ---
+ is_color_light: function(color_string) {
+ const debug = window.StyleRT.debug;
+
+ // 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) {
+ // debug.warn('color_layout', `Failed to parse color: "${color_string}". Defaulting to Light.`);
+ 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
+/*
+ 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).
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.TOC = function(){
+ const debug = window.StyleRT.debug || { log: function(){} };
+ const TOC_seq = document.querySelectorAll('rt-toc');
+
+ TOC_seq.forEach( (container ,TOC_index) => {
+ container.style.display = 'block';
+
+ // 1. Determine Target Level
+ const attr_level = parseInt( container.getAttribute('level') );
+ let target_level;
+
+ if( !isNaN(attr_level) ){
+ // EXPLICIT MODE
+ target_level = attr_level;
+ if(debug.log) debug.log('TOC' ,`TOC #${TOC_index} explicit target: H${target_level}`);
+ } else {
+ // IMPLICIT / CONTEXT MODE
+ let context_level = 0; // Default 0 (Root)
+ 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;
+ }
+ target_level = context_level + 1;
+ if(debug.log) debug.log('TOC' ,`TOC #${TOC_index} context implied target: H${target_level}`);
+ }
+
+ // Stop condition: Stop if we hit a heading that is a "parent" or "sibling" of the context.
+ // Mathematically: Stop if found_level < target_level.
+ const stop_threshold = target_level;
+
+ // 2. Setup Container
+ container.innerHTML = '';
+ const title = document.createElement('h1');
+ // Title logic: If targeting H1, the element serves as a Main TOC. Otherwise the element serves as a Section TOC.
+ title.textContent = target_level === 1 ? 'Table of Contents' : 'Section Contents';
+ title.style.textAlign = 'center';
+ container.appendChild(title);
+
+ const list = document.createElement('ul');
+ list.style.listStyle = 'none';
+ list.style.paddingLeft = '0';
+ container.appendChild(list);
+
+ // 3. Scan Forward
+ 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 Logic:
+ // If we are looking for H2s, we stop if we hit an H1 (level 1).
+ // If we are looking for H1s, we stop if we hit nothing (level 0).
+ if(found_level < target_level){
+ break;
+ }
+
+ // COLLECT Logic:
+ if(found_level === target_level){
+ if(!next_el.id) next_el.id = `TOC-ref-${TOC_index}-${found_level}-${list.children.length}`;
+
+ const li = document.createElement('li');
+ li.style.marginBottom = '0.5rem';
+
+ const a = document.createElement('a');
+ a.href = `#${next_el.id}`;
+ a.textContent = next_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);
+ list.appendChild(li);
+ }
+ }
+ next_el = next_el.nextElementSibling;
+ }
+ });
+};
--- /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.StyleRT;
+ 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.StyleRT = window.StyleRT || {};
+window.StyleRT.code = code;
--- /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.StyleRT = window.StyleRT || {};
+window.StyleRT.math = math;
--- /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.StyleRT = window.StyleRT || {};
+
+window.StyleRT.term = function() {
+ const RT = window.StyleRT;
+
+ 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="..."></RT-title>
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.title = function() {
+ const debug = window.StyleRT.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');
+
+ 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);
+ }
+
+ // Replace the raw tag with the generated block
+ el.replaceWith(container);
+ });
+};
--- /dev/null
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // 1. Declare Dependencies with updated core/ and layout/ paths
+ RT.include('RT/core/utility');
+ RT.include('RT/element/math');
+ RT.include('RT/element/code');
+ RT.include('RT/element/term');
+ RT.include('RT/element/TOC');
+ RT.include('RT/element/title');
+ RT.include('RT/element/theme_selector');
+ RT.include('RT/layout/paginate_by_element');
+ RT.include('RT/layout/page_fixed_glow');
+ RT.include('RT/core/body_visibility_visible');
+
+ // 2. The Typography Layout (Adapted from the original)
+ 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)";
+ }
+
+ // CSS injection for headers, lists, etc., goes here
+ // (Omitted for brevity, but carried over from the original file)
+ };
+
+ // 3. The Execution Sequence
+ const run_semantics = function(){
+ if(RT.theme) RT.theme();
+ RT.article();
+
+ // Call the newly renamed element functions
+ if(RT.title) RT.title();
+ if(RT.term) RT.term();
+ if(RT.math) RT.math();
+ if(RT.code) RT.code();
+
+ // Check for MathJax typesetting
+ if(window.MathJax && MathJax.Hub && MathJax.Hub.Queue){
+ MathJax.Hub.Queue( ["Typeset" ,MathJax.Hub] ,run_layout );
+ }else{
+ run_layout();
+ }
+ };
+
+ const run_layout = function(){
+ if(RT.TOC) RT.TOC();
+ if(RT.paginate_by_element) RT.paginate_by_element();
+ if(RT.page) RT.page();
+ if(RT.body_visibility_visible) RT.body_visibility_visible();
+ };
+
+ // 4. Bind to DOM Ready
+ // This replaces the need to put a script at the bottom of the HTML body
+ document.addEventListener( 'DOMContentLoaded' ,run_semantics );
+
+})();
--- /dev/null
+/*
+ <head>
+ <script>
+ window.StyleRT.include('RT/layout/memo_state_dept');
+ </script>
+ </head>
+ <body>
+ <RT-memo>
+ </RT-memo>
+ </body>
+*/
+
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // 1. Declare Dependencies
+ RT.include('RT/core/utility');
+ RT.include('RT/element/title');
+ RT.include('RT/element/term');
+ RT.include('RT/element/TOC');
+ RT.include('RT/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 fixed-height container with a glowing border effect that matches the active theme.
+*/
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // Function name stays generic so the orchestrator can call RT.page() regardless of file choice
+ RT.page = function() {
+ RT.config = RT.config || {};
+
+ // Default Configuration
+ // We use CSS Variables here so the Theme controls the actual colors.
+ const defaults = {
+ width: "100%"
+ ,height: "1056px"
+ ,padding: "3rem"
+ ,margin: "4rem auto"
+
+ // Dynamic Theme Bindings
+ ,bg_color: "var(--rt-surface-0)" // Black (Dark) or Cream (Light)
+ ,border_color: "var(--rt-brand-primary)" // The Main Accent Color
+ ,text_color: "var(--rt-brand-primary)" // Page Number Color
+
+ // The Glow: Uses the primary brand color for the shadow
+ ,shadow: "drop-shadow(0px 0px 15px var(--rt-brand-primary))"
+ };
+
+ // Merge defaults
+ 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};
+ height: ${conf.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; /* Prevent interference with clicks */
+ }
+ `;
+ document.head.appendChild(style_el);
+ }
+ };
+})();
--- /dev/null
+/*
+ Layout Paginator: paginate_by_element
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.paginate_by_element = function() {
+ const RT = window.StyleRT;
+
+ // Fix: Read safely without overwriting the config namespace
+ const page_conf = (RT.config && RT.config.page) ? RT.config.page : {};
+ const page_height_limit = page_conf.height_limit || 1000;
+
+ const article_seq = document.querySelectorAll("RT-article");
+
+ // HURDLE: Error if no articles found to paginate
+ if(article_seq.length === 0) {
+ RT.debug.error('pagination', 'No <RT-article> elements found. Pagination aborted.');
+ return;
+ }
+
+ article_seq.forEach( (article) => {
+ const raw_elements = Array.from(article.children).filter(el =>
+ !['SCRIPT', 'STYLE', 'RT-PAGE'].includes(el.tagName)
+ );
+
+ if(raw_elements.length === 0) return;
+
+ const pages = [];
+ let current_batch = [];
+ let current_h = 0;
+
+ const get_el_height = (el) => {
+ const rect = el.getBoundingClientRect();
+ const style = window.getComputedStyle(el);
+ const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom);
+ return (rect.height || 0) + (margin || 0);
+ };
+
+ for (let i = 0; i < raw_elements.length; i++) {
+ const el = raw_elements[i];
+ const h = get_el_height(el);
+ const is_heading = /^H[1-6]/.test(el.tagName);
+
+ let total_required_h = h;
+ if (is_heading && i + 1 < raw_elements.length) {
+ total_required_h += get_el_height(raw_elements[i + 1]);
+ }
+
+ if (current_h + total_required_h > page_height_limit && current_batch.length > 0) {
+ pages.push(current_batch);
+ current_batch = [];
+ current_h = 0;
+ }
+
+ current_batch.push(el);
+ current_h += h;
+ }
+
+ if (current_batch.length > 0) pages.push(current_batch);
+
+ article.innerHTML = '';
+
+ pages.forEach( (list, index) => {
+ const page_el = document.createElement('rt-page');
+ page_el.id = `page-${index+1}`;
+ list.forEach(item => page_el.appendChild(item));
+ article.appendChild(page_el);
+ });
+
+ if (RT.debug) RT.debug.log('pagination', `Article paginated into ${pages.length} pages.`);
+ });
+};
--- /dev/null
+/*
+ Theme: Inverse Wheat (Dark)
+ Standard: Theme 1.0
+ Description: High contrast Amber on Deep Charcoal.
+*/
+( function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ 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); }
+ `;
+ 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.StyleRT = window.StyleRT || {};
+
+ 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.StyleRT = window.StyleRT || {};
+
+ 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
+/*
+ Targets the root element to ensure total blackout during load.
+*/
+function body_visibility_hidden(){
+ const gate = document.createElement('style');
+ gate.id = 'rt-visibility-gate';
+ gate.textContent = 'html{visibility:hidden !important; background:black !important;}';
+ document.head.appendChild(gate);
+}
+
+window.StyleRT = window.StyleRT || {};
+window.StyleRT.body_visibility_hidden = body_visibility_hidden;
--- /dev/null
+/*
+ Restores visibility by removing the visibility gate.
+*/
+function body_visibility_visible(){
+ const gate = document.getElementById('rt-visibility-gate');
+ if (gate){
+ gate.remove();
+ }
+ document.body.style.visibility = 'visible';
+}
+
+window.StyleRT = window.StyleRT || {};
+window.StyleRT.body_visibility_visible = body_visibility_visible;
--- /dev/null
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.include = function(path_identifier){
+ let parts_seq = path_identifier.split('/');
+ let namespace = parts_seq[0];
+
+ let module_path = parts_seq.slice(1).join('/');
+
+ if(module_path === 'theme'){
+ let saved_theme = localStorage.getItem('RT_theme_preference');
+ if(!saved_theme){
+ saved_theme = 'dark_gold';
+ localStorage.setItem('RT_theme_preference' ,saved_theme);
+ }
+ module_path = 'theme/' + saved_theme;
+ }
+
+ let base_path = window.StyleRT_namespaces[namespace];
+ if(!base_path){
+ console.error("Namespace not found: " + namespace);
+ return;
+ }
+
+ let full_path = base_path + '/' + module_path + '.js';
+ // FIXED: No backslashes in the closing script tag
+ document.write('<script src="' + full_path + '"></script>');
+};
--- /dev/null
+/*
+ General utilities for the StyleRT library.
+*/
+
+window.StyleRT = window.StyleRT || {};
+
+// --- DEBUG SYSTEM ---
+window.StyleRT.debug = {
+
+ // all debug messages enabled
+/*
+ active_tokens: new Set([
+ 'style', 'layout', 'pagination'
+ ,'selector', 'config', 'error'
+ ,'term'
+ ]),
+*/
+ active_tokens: new Set([
+ 'term'
+ ]),
+
+ log: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.log(`[StyleRT:${token}]`, message);
+ }
+ },
+
+ warn: function(token, message) {
+ if (this.active_tokens.has(token)) {
+ console.warn(`[StyleRT:${token}]`, message);
+ }
+ },
+
+ // New: Always log errors regardless of token, but tag them
+ error: function(token, message) {
+ console.error(`[StyleRT:${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.StyleRT.utility = {
+ // --- FONT PHYSICS ---
+ measure_ink_ratio: function(target_font, ref_font = null) {
+ const debug = window.StyleRT.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;
+ // debug.log('layout', `Ink Ratio calculated: ${ratio.toFixed(3)}`);
+
+ return {
+ ratio: ratio,
+ baseline_diff: ref_m.descent - target_m.descent
+ };
+ },
+
+ // --- COLOR PHYSICS ---
+ is_color_light: function(color_string) {
+ const debug = window.StyleRT.debug;
+
+ // 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) {
+ // debug.warn('color_layout', `Failed to parse color: "${color_string}". Defaulting to Light.`);
+ 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
+/*
+ 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).
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.TOC = function(){
+ const debug = window.StyleRT.debug || { log: function(){} };
+ const TOC_seq = document.querySelectorAll('rt-toc');
+
+ TOC_seq.forEach( (container ,TOC_index) => {
+ container.style.display = 'block';
+
+ // 1. Determine Target Level
+ const attr_level = parseInt( container.getAttribute('level') );
+ let target_level;
+
+ if( !isNaN(attr_level) ){
+ // EXPLICIT MODE
+ target_level = attr_level;
+ if(debug.log) debug.log('TOC' ,`TOC #${TOC_index} explicit target: H${target_level}`);
+ } else {
+ // IMPLICIT / CONTEXT MODE
+ let context_level = 0; // Default 0 (Root)
+ 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;
+ }
+ target_level = context_level + 1;
+ if(debug.log) debug.log('TOC' ,`TOC #${TOC_index} context implied target: H${target_level}`);
+ }
+
+ // Stop condition: Stop if we hit a heading that is a "parent" or "sibling" of the context.
+ // Mathematically: Stop if found_level < target_level.
+ const stop_threshold = target_level;
+
+ // 2. Setup Container
+ container.innerHTML = '';
+ const title = document.createElement('h1');
+ // Title logic: If targeting H1, the element serves as a Main TOC. Otherwise the element serves as a Section TOC.
+ title.textContent = target_level === 1 ? 'Table of Contents' : 'Section Contents';
+ title.style.textAlign = 'center';
+ container.appendChild(title);
+
+ const list = document.createElement('ul');
+ list.style.listStyle = 'none';
+ list.style.paddingLeft = '0';
+ container.appendChild(list);
+
+ // 3. Scan Forward
+ 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 Logic:
+ // If we are looking for H2s, we stop if we hit an H1 (level 1).
+ // If we are looking for H1s, we stop if we hit nothing (level 0).
+ if(found_level < target_level){
+ break;
+ }
+
+ // COLLECT Logic:
+ if(found_level === target_level){
+ if(!next_el.id) next_el.id = `TOC-ref-${TOC_index}-${found_level}-${list.children.length}`;
+
+ const li = document.createElement('li');
+ li.style.marginBottom = '0.5rem';
+
+ const a = document.createElement('a');
+ a.href = `#${next_el.id}`;
+ a.textContent = next_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);
+ list.appendChild(li);
+ }
+ }
+ next_el = next_el.nextElementSibling;
+ }
+ });
+};
--- /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.StyleRT;
+ 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.StyleRT = window.StyleRT || {};
+window.StyleRT.code = code;
--- /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.StyleRT = window.StyleRT || {};
+window.StyleRT.math = math;
--- /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.StyleRT = window.StyleRT || {};
+
+window.StyleRT.term = function() {
+ const RT = window.StyleRT;
+
+ 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="..."></RT-title>
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.title = function() {
+ const debug = window.StyleRT.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');
+
+ 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);
+ }
+
+ // Replace the raw tag with the generated block
+ el.replaceWith(container);
+ });
+};
--- /dev/null
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // 1. Declare Dependencies with updated core/ and layout/ paths
+ RT.include('RT/core/utility');
+ RT.include('RT/element/math');
+ RT.include('RT/element/code');
+ RT.include('RT/element/term');
+ RT.include('RT/element/TOC');
+ RT.include('RT/element/title');
+ RT.include('RT/element/theme_selector');
+ RT.include('RT/layout/paginate_by_element');
+ RT.include('RT/layout/page_fixed_glow');
+ RT.include('RT/core/body_visibility_visible');
+
+ // 2. The Typography Layout (Adapted from the original)
+ 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)";
+ }
+
+ // CSS injection for headers, lists, etc., goes here
+ // (Omitted for brevity, but carried over from the original file)
+ };
+
+ // 3. The Execution Sequence
+ const run_semantics = function(){
+ if(RT.theme) RT.theme();
+ RT.article();
+
+ // Call the newly renamed element functions
+ if(RT.title) RT.title();
+ if(RT.term) RT.term();
+ if(RT.math) RT.math();
+ if(RT.code) RT.code();
+
+ // Check for MathJax typesetting
+ if(window.MathJax && MathJax.Hub && MathJax.Hub.Queue){
+ MathJax.Hub.Queue( ["Typeset" ,MathJax.Hub] ,run_layout );
+ }else{
+ run_layout();
+ }
+ };
+
+ const run_layout = function(){
+ if(RT.TOC) RT.TOC();
+ if(RT.paginate_by_element) RT.paginate_by_element();
+ if(RT.page) RT.page();
+ if(RT.body_visibility_visible) RT.body_visibility_visible();
+ };
+
+ // 4. Bind to DOM Ready
+ // This replaces the need to put a script at the bottom of the HTML body
+ document.addEventListener( 'DOMContentLoaded' ,run_semantics );
+
+})();
--- /dev/null
+/*
+ <head>
+ <script>
+ window.StyleRT.include('RT/layout/memo_state_dept');
+ </script>
+ </head>
+ <body>
+ <RT-memo>
+ </RT-memo>
+ </body>
+*/
+
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // 1. Declare Dependencies
+ RT.include('RT/core/utility');
+ RT.include('RT/element/title');
+ RT.include('RT/element/term');
+ RT.include('RT/element/TOC');
+ RT.include('RT/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 fixed-height container with a glowing border effect that matches the active theme.
+*/
+(function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ // Function name stays generic so the orchestrator can call RT.page() regardless of file choice
+ RT.page = function() {
+ RT.config = RT.config || {};
+
+ // Default Configuration
+ // We use CSS Variables here so the Theme controls the actual colors.
+ const defaults = {
+ width: "100%"
+ ,height: "1056px"
+ ,padding: "3rem"
+ ,margin: "4rem auto"
+
+ // Dynamic Theme Bindings
+ ,bg_color: "var(--rt-surface-0)" // Black (Dark) or Cream (Light)
+ ,border_color: "var(--rt-brand-primary)" // The Main Accent Color
+ ,text_color: "var(--rt-brand-primary)" // Page Number Color
+
+ // The Glow: Uses the primary brand color for the shadow
+ ,shadow: "drop-shadow(0px 0px 15px var(--rt-brand-primary))"
+ };
+
+ // Merge defaults
+ 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};
+ height: ${conf.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; /* Prevent interference with clicks */
+ }
+ `;
+ document.head.appendChild(style_el);
+ }
+ };
+})();
--- /dev/null
+/*
+ Layout Paginator: paginate_by_element
+*/
+window.StyleRT = window.StyleRT || {};
+
+window.StyleRT.paginate_by_element = function() {
+ const RT = window.StyleRT;
+
+ // Fix: Read safely without overwriting the config namespace
+ const page_conf = (RT.config && RT.config.page) ? RT.config.page : {};
+ const page_height_limit = page_conf.height_limit || 1000;
+
+ const article_seq = document.querySelectorAll("RT-article");
+
+ // HURDLE: Error if no articles found to paginate
+ if(article_seq.length === 0) {
+ RT.debug.error('pagination', 'No <RT-article> elements found. Pagination aborted.');
+ return;
+ }
+
+ article_seq.forEach( (article) => {
+ const raw_elements = Array.from(article.children).filter(el =>
+ !['SCRIPT', 'STYLE', 'RT-PAGE'].includes(el.tagName)
+ );
+
+ if(raw_elements.length === 0) return;
+
+ const pages = [];
+ let current_batch = [];
+ let current_h = 0;
+
+ const get_el_height = (el) => {
+ const rect = el.getBoundingClientRect();
+ const style = window.getComputedStyle(el);
+ const margin = parseFloat(style.marginTop) + parseFloat(style.marginBottom);
+ return (rect.height || 0) + (margin || 0);
+ };
+
+ for (let i = 0; i < raw_elements.length; i++) {
+ const el = raw_elements[i];
+ const h = get_el_height(el);
+ const is_heading = /^H[1-6]/.test(el.tagName);
+
+ let total_required_h = h;
+ if (is_heading && i + 1 < raw_elements.length) {
+ total_required_h += get_el_height(raw_elements[i + 1]);
+ }
+
+ if (current_h + total_required_h > page_height_limit && current_batch.length > 0) {
+ pages.push(current_batch);
+ current_batch = [];
+ current_h = 0;
+ }
+
+ current_batch.push(el);
+ current_h += h;
+ }
+
+ if (current_batch.length > 0) pages.push(current_batch);
+
+ article.innerHTML = '';
+
+ pages.forEach( (list, index) => {
+ const page_el = document.createElement('rt-page');
+ page_el.id = `page-${index+1}`;
+ list.forEach(item => page_el.appendChild(item));
+ article.appendChild(page_el);
+ });
+
+ if (RT.debug) RT.debug.log('pagination', `Article paginated into ${pages.length} pages.`);
+ });
+};
--- /dev/null
+/*
+ Theme: Inverse Wheat (Dark)
+ Standard: Theme 1.0
+ Description: High contrast Amber on Deep Charcoal.
+*/
+( function(){
+ const RT = window.StyleRT = window.StyleRT || {};
+
+ 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); }
+ `;
+ 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.StyleRT = window.StyleRT || {};
+
+ 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.StyleRT = window.StyleRT || {};
+
+ 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
-#include <stdio.h>
-int main(void){ puts("hello from Rabbit CLI"); return 0; }
# input guards
setup_must_be="developer/tool/setup"
- if [ "$ENV" != "$env_must_be" ]; then
- echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ if [ "$SETUP" != "$setup_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $setup_must_be environment"
exit 1
fi
# input guards
setup_must_be="developer/tool/setup"
- if [ "$ENV" != "$env_must_be" ]; then
- echo "$(script_fp):: error: must be run in the $env_must_be environment"
+ if [ "$SETUP" != "$setup_must_be" ]; then
+ echo "$(script_fp):: error: must be run in the $setup_must_be environment"
exit 1
fi
cd "$REPO_HOME"/developer || exit 1
# /bin/make -f tool/makefile $@
- mkdir -p scratchpad/authored
- cp authored/HTTP_server.js scratchpad/authored/
- cp authored/port_forwarder scratchpad/authored/
- cp authored/port_forwarder_CLI scratchpad/authored/
- cp authored/permission_RT scratchpad/authored/
- cp authored/permission_UPG scratchpad/authored/
+ mkdir -p scratchpad/stage && cp -au authored/RT scratchpad/stage/
set +x
echo "$(script_fn) done."
import os ,sys ,shutil ,stat ,pwd ,grp ,glob ,tempfile
HELP = """usage: release {write|clean|ls|diff|help|dry write}
- write Writes promoted files from scratchpad/stage into user/release. Only updates newer files.
- clean Remove all contents of the user/release directory.
- ls List user/release as an indented tree: PERMS OWNER NAME.
- diff List files in user/release that are not in scratchpad/stage, or are newer.
+ write Writes promoted files from scratchpad/stage into consumer/release. Only updates newer files.
+ clean Remove all contents of the consumer/release directory.
+ ls List consumer/release as an indented tree: PERMS OWNER NAME.
+ diff List files in consumer/release that are not in scratchpad/stage, or are newer.
help Show this message.
dry write Preview what write would do without modifying the filesystem.
"""
print(f"release: {msg}" ,file=sys.stderr)
sys.exit(code)
-def assert_env():
- env = os.environ.get("ENV" ,"")
- if(env != ENV_MUST_BE):
+def assert_setup():
+ setup_val = os.environ.get("SETUP" ,"")
+ if(setup_val != SETUP_MUST_BE):
hint = (
"SETUP is not 'developer/tool/setup'.\n"
"Enter the project with: . setup developer\n"
"That script exports: ROLE=developer; SETUP=$ROLE/tool/setup"
)
- exit_with_status(f"bad environment: ENV='{env}'. {hint}")
+ exit_with_status(f"bad environment: SETUP='{setup_val}'. {hint}")
def repo_home():
rh = os.environ.get("REPO_HOME")
,*parts
)
-def upath(*parts):
+def cpath(*parts):
return os.path.join(
repo_home()
- ,"user"
+ ,"consumer"
,"release"
,*parts
)
def dev_root():
return dpath()
-def user_root():
- return upath()
+def consumer_root():
+ return cpath()
def _display_src(p_abs: str) -> str:
try:
try:
rel = os.path.relpath(
p_abs
- ,user_root()
+ ,consumer_root()
)
rel = "" if rel == "." else rel
- return "$REPO_HOME/user/release" + ("/" + rel if rel else "")
+ return "$REPO_HOME/consumer/release" + ("/" + rel if rel else "")
except Exception:
return p_abs
def ensure_dir(path_str ,mode=DEFAULT_DIR_MODE ,dry=False):
if(dry):
if( not os.path.isdir(path_str) ):
- shown = _display_dst(path_str) if path_str.startswith(user_root()) else (
+ shown = _display_dst(path_str) if path_str.startswith(consumer_root()) else (
os.path.relpath(path_str ,dev_root()) if path_str.startswith(dev_root()) else path_str
)
print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
if( len(ownergrp) > ogw ):
ogw = len(ownergrp)
- print("user/release/")
+ print("consumer/release/")
for isdir ,depth ,perms ,ownergrp ,name in entries:
indent = " " * depth
print(f"{perms} {ownergrp:<{ogw}} {indent}{name}")
print(f"+ install -m {oct(mode)[2:]} '{src_show}' '{dst_show}'")
def cmd_write(dry=False):
- assert_env()
- ensure_dir(upath() ,DEFAULT_DIR_MODE ,dry=dry)
+ assert_setup()
+ ensure_dir(cpath() ,DEFAULT_DIR_MODE ,dry=dry)
src_root = dpath(
"scratchpad"
for fn in files:
src_abs = os.path.join(root ,fn)
rel = os.path.relpath(src_abs ,src_root)
- dst_abs = os.path.join(upath() ,rel)
+ dst_abs = os.path.join(cpath() ,rel)
if( os.path.exists(dst_abs) ):
src_mtime = os.stat(src_abs).st_mtime
print(f"(info) nothing new to promote from {_display_src(src_root)}")
def cmd_diff():
- assert_env()
- dst_root = upath()
+ assert_setup()
+ dst_root = cpath()
src_root = dpath(
"scratchpad"
,"stage"
print("No differences found. Release matches stage.")
def cmd_clean():
- assert_env()
- user_root_dir = upath()
- if( not os.path.isdir(user_root_dir) ):
+ assert_setup()
+ consumer_root_dir = cpath()
+ if( not os.path.isdir(consumer_root_dir) ):
return
- for name in os.listdir(user_root_dir):
- p = os.path.join(user_root_dir ,name)
+ for name in os.listdir(consumer_root_dir):
+ p = os.path.join(consumer_root_dir ,name)
if( os.path.isdir(p) and not os.path.islink(p) ):
shutil.rmtree(p ,ignore_errors=True)
else:
elif(cmd == "clean"):
cmd_clean()
elif(cmd == "ls"):
- list_tree(upath())
+ list_tree(cpath())
elif(cmd == "diff"):
cmd_diff()
elif(cmd == "help"):