<head>
<meta charset="UTF-8">
<title>Subu User Containers</title>
- <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-
- <script src="style/body_visibility_hidden.js"></script>
<script>
- window.StyleRT.body_visibility_hidden();
+ {
+ let dirpr_library = null;
+ try{ dirpr_library = localStorage.getItem("RT.dirpr_library"); }catch(e){}
+ window.RT = window.RT || {};
+ RT.dirpr_library = RT.dirpr_library || dirpr_library || "https://style.ReasoningTechnology.com/Manuscript";
+ document.write('<script src="' + RT.dirpr_library + '/Core/RT-Manuscript_make.js"><\/script>');
+ }
+ </script>
+ <script>
+ window.RT.theme_preference('inverse_wheat');
+ window.RT.load('Layout/paginate');
+ window.RT.load('Layout/article_tech_ref');
+ window.RT.load('Element/theme_selector');
</script>
</head>
<body>
- <RT-article>
- <RT-title
+ <RT·theme-selector></RT·theme-selector>
+ <RT·article>
+ <RT·title
author="Thomas Walker Lynch"
date="2026-03-02 20:38"
title="Subu User Containers">
- </RT-title>
+ </RT·title>
- <RT-TOC level="1"></RT-TOC>
+ <RT·TOC level="1"></RT·TOC>
<h1>Introduction</h1>
<p>
- A <RT-neologism>Subu</RT-neologism> is an isolated user environment operating within a broader parent system. It functions as a strict <RT-neologism>User Container</RT-neologism>. Each subu possesses its own namespace, isolated processes, and dedicated I/O resources.
+ A <RT·neologism>Subu</RT·neologism> is an isolated user environment operating within a broader parent system. It functions as a strict <RT·neologism>User Container</RT·neologism>. Each subu possesses its own namespace, isolated processes, and dedicated I/O resources.
</p>
<p>
- The core architectural goal of the subu system is administration without elevation. A <RT-neologism>Master User</RT-neologism> has the ability to completely administer a subordinate user environment without requiring <RT-code>sudo</RT-code> privileges or system-wide root access. The master user accomplishes this through a dedicated suite of tools and a specialized file system architecture.
+ The core architectural goal of the subu system is administration without elevation. A <RT·neologism>Master User</RT·neologism> has the ability to completely administer a subordinate user environment without requiring <RT·code>sudo</RT·code> privileges or system-wide root access. The master user accomplishes this through a dedicated suite of tools and a specialized file system architecture.
</p>
<h1>Design Decisions and Architecture</h1>
<h2>Namespacing</h2>
<p>
- Subu accounts require a naming convention that links them logically to their master user while remaining fully compliant with standard Linux utilities. The system utilizes the middle dot (<RT-code>·</RT-code>) as the namespace separator.
+ Subu accounts require a naming convention that links them logically to their master user while remaining fully compliant with standard Linux utilities. The system utilizes the middle dot (<RT·code>·</RT·code>) as the namespace separator.
</p>
<p>
- An example account name is <RT-code>Thomas·developer</RT-code>.
+ An example account name is <RT·code>Thomas·developer</RT·code>.
</p>
<p>
- The middle dot offers several distinct advantages over traditional separators. The colon (<RT-code>:</RT-code>) is strictly forbidden, as it acts as the field delimiter in <RT-code>/etc/passwd</RT-code> and will corrupt the system configuration. The hyphen (<RT-code>-</RT-code>) and underscore (<RT-code>_</RT-code>) are heavily utilized in standard user naming conventions, increasing the probability of accidental collisions. The middle dot is visually clean, requires a specific <RT-code>--badname</RT-code> flag during account creation, and presents a typing barrier for standard users, practically eliminating accidental naming conflicts.
+ The middle dot offers several distinct advantages over traditional separators. The colon (<RT·code>:</RT·code>) is strictly forbidden, as it acts as the field delimiter in <RT·code>/etc/passwd</RT·code> and will corrupt the system configuration. The hyphen (<RT·code>-</RT·code>) and underscore (<RT·code>_</RT·code>) are heavily utilized in standard user naming conventions, increasing the probability of accidental collisions. The middle dot is visually clean, requires a specific <RT·code>--badname</RT·code> flag during account creation, and presents a typing barrier for standard users, practically eliminating accidental naming conflicts.
</p>
<h2>The File System Dilemma</h2>
<p>
- Attempting to nest subu home directories within the master user's home directory (e.g., <RT-code>/home/Thomas/subu/developer</RT-code>) creates an intractable conflict with <RT-term>POSIX</RT-term> permissions and <RT-term>Access Control Lists</RT-term> (ACLs).
+ Attempting to nest subu home directories within the master user's home directory (e.g., <RT·code>/home/Thomas/subu/developer</RT·code>) creates an intractable conflict with <RT·term>POSIX</RT·term> permissions and <RT·term>Access Control Lists</RT·term> (ACLs).
</p>
<p>
- If a subu is nested, the operating system requires group execute permissions on the master's home directory to allow traversal. If a person attempts to restrict this using ACL masks, the subu retains sovereign control over its own files. A subu executing a standard <RT-code>chmod 600</RT-code> on a file recalculates the ACL mask to zero, instantly severing the master user's access to that file and causing subsequent system backups to fail with permission errors.
+ If a subu is nested, the operating system requires group execute permissions on the master's home directory to allow traversal. If a person attempts to restrict this using ACL masks, the subu retains sovereign control over its own files. A subu executing a standard <RT·code>chmod 600</RT·code> on a file recalculates the ACL mask to zero, instantly severing the master user's access to that file and causing subsequent system backups to fail with permission errors.
</p>
<h2>The Dual Layer Architecture</h2>
<h3>The Physical Layer</h3>
<p>
- Every subu home directory resides directly under <RT-code>/home</RT-code>, entirely flat and parallel to the master user account.
+ Every subu home directory resides directly under <RT·code>/home</RT·code>, entirely flat and parallel to the master user account.
</p>
- <RT-code>
+ <RT·code>
/home/Thomas
/home/Thomas·developer
- </RT-code>
+ </RT·code>
<p>
Placing the directories side by side eliminates traversal conflicts. The subu relies purely on standard POSIX ownership of its dedicated path, ensuring absolute sovereign control over its workspace and preventing permission changes from breaking the directory structure.
<h3>The Management Layer</h3>
<p>
- To grant the master user administrative access without violating the physical layer permissions, the system utilizes <RT-term>ID-mapped mounts</RT-term>. </p>
+ To grant the master user administrative access without violating the physical layer permissions, the system utilizes <RT·term>ID-mapped mounts</RT·term>. </p>
<p>
A system service establishes a mount point within the master user's workspace, pointing to the subu's physical directory:
</p>
- <RT-code>
+ <RT·code>
/home/Thomas/subu/Thomas·developer -> /home/Thomas·developer
- </RT-code>
+ </RT·code>
<p>
The kernel translates the subu's user ID to the master user's user ID in real-time. Through this mount, the master user perceives absolute ownership of all subu files. If the subu locks down a file in the physical layer, the master user retains the ability to read, modify, or repair it through the management layer.
<h2>Hardware Access and Audio Seating</h2>
<p>
- Because subu accounts utilize <RT-code>logind</RT-code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and graphics nodes) to the active seated user. The first user to claim the audio socket locks out all other background sessions.
+ Because subu accounts utilize <RT·code>logind</RT·code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and graphics nodes) to the active seated user. The first user to claim the audio socket locks out all other background sessions.
</p>
<p>
</p>
<p>
- If a subu or master user creates an ID-mapped mount directly during their session, the Linux login daemon (<RT-code>systemd-logind</RT-code>) will aggressively terminate the background FUSE processes upon logout, destroying the management layer.
+ If a subu or master user creates an ID-mapped mount directly during their session, the Linux login daemon (<RT·code>systemd-logind</RT·code>) will aggressively terminate the background FUSE processes upon logout, destroying the management layer.
</p>
<p>
- The system setup process executes independently of user sessions. It queries a central SQLite database to determine the registered subu relationships, constructs the <RT-code>/home/master/subu</RT-code> directory structures, and establishes the ID-mapped mounts. Crucially, the setup process assigns ownership of these mounts to the master account. Because the mounts are created by a boot-level service, they persist indefinitely, surviving all individual user logouts.
+ The system setup process executes independently of user sessions. It queries a central SQLite database to determine the registered subu relationships, constructs the <RT·code>/home/master/subu</RT·code> directory structures, and establishes the ID-mapped mounts. Crucially, the setup process assigns ownership of these mounts to the master account. Because the mounts are created by a boot-level service, they persist indefinitely, surviving all individual user logouts.
</p>
<h1>User Manual</h1>
<h3>For the Subu</h3>
<ul>
- <li><strong>Home Directory:</strong> <RT-code>/home/Thomas·developer</RT-code></li>
+ <li><strong>Home Directory:</strong> <RT·code>/home/Thomas·developer</RT·code></li>
<li><strong>Perspective:</strong> The subu operates in a standard Linux environment. Files are owned by the subu. All paths behave normally.</li>
</ul>
<h3>For the Master User</h3>
<ul>
- <li><strong>Primary Workspace:</strong> <RT-code>/home/Thomas</RT-code></li>
- <li><strong>Subu Management Directory:</strong> <RT-code>/home/Thomas/subu/Thomas·developer</RT-code></li>
- <li><strong>Perspective:</strong> The master user navigates into the <RT-code>subu</RT-code> directory to administer subordinate accounts. All files within this directory appear to be owned by the master user. Modifying a file here instantly modifies the physical file for the subu.</li>
+ <li><strong>Primary Workspace:</strong> <RT·code>/home/Thomas</RT·code></li>
+ <li><strong>Subu Management Directory:</strong> <RT·code>/home/Thomas/subu/Thomas·developer</RT·code></li>
+ <li><strong>Perspective:</strong> The master user navigates into the <RT·code>subu</RT·code> directory to administer subordinate accounts. All files within this directory appear to be owned by the master user. Modifying a file here instantly modifies the physical file for the subu.</li>
</ul>
<h2>Backups</h2>
<p>
- When a master user initiates a backup of their home directory, they must instruct the archiving tool (such as <RT-code>tar</RT-code> or <RT-code>rsync</RT-code>) to respect filesystem boundaries. Passing the <RT-code>--one-file-system</RT-code> flag prevents the backup utility from traversing into the <RT-code>/home/Thomas/subu</RT-code> mounts, avoiding infinite recursion loops and duplicate data capture. The physical subu directories (<RT-code>/home/Thomas·developer</RT-code>) are backed up via separate, independent archive routines.
+ When a master user initiates a backup of their home directory, they must instruct the archiving tool (such as <RT·code>tar</RT·code> or <RT·code>rsync</RT·code>) to respect filesystem boundaries. Passing the <RT·code>--one-file-system</RT·code> flag prevents the backup utility from traversing into the <RT·code>/home/Thomas/subu</RT·code> mounts, avoiding infinite recursion loops and duplicate data capture. The physical subu directories (<RT·code>/home/Thomas·developer</RT·code>) are backed up via separate, independent archive routines.
</p>
- <script src="style/style_orchestrator.js"></script>
- <script>
- window.StyleRT.style_orchestrator();
- </script>
<h2>Hardware Access and the Virtualization Horizon</h2>
<p>
- Currently, because subu accounts utilize <RT-code>logind</RT-code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and primary graphics nodes) to the active seated user.
+ Currently, because subu accounts utilize <RT·code>logind</RT·code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and primary graphics nodes) to the active seated user.
</p>
<p>
Fundamentally, hardware virtualization and user process or memory virtualization are identical solutions. The historical separation between a virtual machine and a user namespace represents an incomplete modernization of the operating system user model. The subu architecture views these concepts as a unified system. Future iterations will seek to integrate an existing Linux GPU virtualization layer directly into the user container, granting each subu parallel, isolated access to the underlying hardware substrate.
</p>
- </RT-article>
+ </RT·article>
</body>
</html>
<head>
<meta charset="UTF-8">
<title>Subu User Containers</title>
- <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
-
- <script src="style/body_visibility_hidden.js"></script>
<script>
- window.StyleRT.body_visibility_hidden();
+ {
+ let dirpr_library = null;
+ try{ dirpr_library = localStorage.getItem("RT.dirpr_library"); }catch(e){}
+ window.RT = window.RT || {};
+ RT.dirpr_library = RT.dirpr_library || dirpr_library || "https://style.ReasoningTechnology.com/Manuscript";
+ document.write('<script src="' + RT.dirpr_library + '/Core/RT-Manuscript_make.js"><\/script>');
+ }
+ </script>
+ <script>
+ window.RT.theme_preference('inverse_wheat');
+ window.RT.load('Layout/paginate');
+ window.RT.load('Layout/article_tech_ref');
+ window.RT.load('Element/theme_selector');
</script>
</head>
<body>
- <RT-article>
- <RT-title
+ <RT·theme-selector></RT·theme-selector>
+ <RT·article>
+ <RT·title
author="Thomas Walker Lynch"
date="2026-03-03 06:08"
title="Subu User Containers">
- </RT-title>
+ </RT·title>
- <RT-TOC level="1"></RT-TOC>
+ <RT·TOC level="1"></RT·TOC>
<h1>Introduction</h1>
<p>
- A <RT-neologism>Subu</RT-neologism> is an isolated user environment operating within a broader parent system. It functions as a strict <RT-neologism>User Container</RT-neologism>. Each subu possesses its own namespace, isolated processes, and dedicated I/O resources.
+ A <RT·neologism>Subu</RT·neologism> is an isolated user environment operating within a broader parent system. It functions as a strict <RT·neologism>User Container</RT·neologism>. Each subu possesses its own namespace, isolated processes, and dedicated I/O resources.
</p>
<p>
- The core architectural goal of the subu system is administration without elevation. A <RT-neologism>Master User</RT-neologism> has the ability to completely administer a subordinate user environment without requiring <RT-code>sudo</RT-code> privileges or system-wide root access. The master user accomplishes this through a dedicated suite of tools and a specialized file system architecture.
+ The core architectural goal of the subu system is administration without elevation. A <RT·neologism>Master User</RT·neologism> has the ability to completely administer a subordinate user environment without requiring <RT·code>sudo</RT·code> privileges or system-wide root access. The master user accomplishes this through a dedicated suite of tools and a specialized file system architecture.
</p>
<h1>Design Decisions and Architecture</h1>
<h2>Namespacing</h2>
<p>
- Subu accounts require a naming convention that links them logically to their master user while remaining fully compliant with legacy Linux utilities. The system utilizes the underscore (<RT-code>_</RT-code>) as the namespace separator, or appends a sequential integer or hash.
+ Subu accounts require a naming convention that links them logically to their master user while remaining fully compliant with legacy Linux utilities. The system utilizes the underscore (<RT·code>_</RT·code>) as the namespace separator, or appends a sequential integer or hash.
</p>
<p>
- Example account names are <RT-code>Thomas_developer</RT-code> and <RT-code>Thomas_incommon</RT-code>.
+ Example account names are <RT·code>Thomas_developer</RT·code> and <RT·code>Thomas_incommon</RT·code>.
</p>
<p>
<h2>The File System Dilemma</h2>
<p>
- Attempting to nest subu home directories within the master user's home directory (e.g., <RT-code>/home/Thomas/subu/developer</RT-code>) creates an intractable conflict with <RT-term>POSIX</RT-term> permissions and <RT-term>Access Control Lists</RT-term> (ACLs).
+ Attempting to nest subu home directories within the master user's home directory (e.g., <RT·code>/home/Thomas/subu/developer</RT·code>) creates an intractable conflict with <RT·term>POSIX</RT·term> permissions and <RT·term>Access Control Lists</RT·term> (ACLs).
</p>
<p>
- If a subu is nested, the operating system requires group execute permissions on the master's home directory to allow traversal. If a person attempts to restrict this using ACL masks, the subu retains sovereign control over its own files. A subu executing a standard <RT-code>chmod 600</RT-code> on a file recalculates the ACL mask to zero, instantly severing the master user's access to that file and causing subsequent system backups to fail with permission errors.
+ If a subu is nested, the operating system requires group execute permissions on the master's home directory to allow traversal. If a person attempts to restrict this using ACL masks, the subu retains sovereign control over its own files. A subu executing a standard <RT·code>chmod 600</RT·code> on a file recalculates the ACL mask to zero, instantly severing the master user's access to that file and causing subsequent system backups to fail with permission errors.
</p>
<h2>The Dual Layer Architecture</h2>
<h3>The Physical Layer</h3>
<p>
- Every subu home directory resides directly under <RT-code>/home</RT-code>, entirely flat and parallel to the master user account.
+ Every subu home directory resides directly under <RT·code>/home</RT·code>, entirely flat and parallel to the master user account.
</p>
- <RT-code>
+ <RT·code>
/home/Thomas
/home/Thomas_developer
- </RT-code>
+ </RT·code>
<p>
Placing the directories side by side eliminates traversal conflicts. The subu relies purely on standard POSIX ownership of its dedicated path, ensuring absolute sovereign control over its workspace and preventing permission changes from breaking the directory structure.
<h3>The Management Layer</h3>
<p>
- To grant the master user administrative access without violating the physical layer permissions, the system utilizes <RT-term>ID-mapped mounts</RT-term>.
+ To grant the master user administrative access without violating the physical layer permissions, the system utilizes <RT·term>ID-mapped mounts</RT·term>.
</p>
<p>
A system service establishes a mount point within the master user's workspace, pointing to the subu's physical directory:
</p>
- <RT-code>
+ <RT·code>
/home/Thomas/subu/developer -> /home/Thomas_developer
- </RT-code>
+ </RT·code>
<p>
The kernel translates the subu's user ID to the master user's user ID in real-time. Through this mount, the master user perceives absolute ownership of all subu files. If the subu locks down a file in the physical layer, the master user retains the ability to read, modify, or repair it through the management layer.
<h2>Hardware Access and the Virtualization Horizon</h2>
<p>
- Currently, because subu accounts utilize <RT-code>logind</RT-code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and primary graphics nodes) to the active seated user.
+ Currently, because subu accounts utilize <RT·code>logind</RT·code> to acquire their own session seats, they inherit strict hardware isolation rules. Modern Linux dynamically assigns hardware ACLs (such as audio and primary graphics nodes) to the active seated user.
</p>
<p>
</p>
<p>
- If a subu or master user creates an ID-mapped mount directly during their session, the Linux login daemon (<RT-code>systemd-logind</RT-code>) will aggressively terminate the background FUSE processes upon logout, destroying the management layer.
+ If a subu or master user creates an ID-mapped mount directly during their session, the Linux login daemon (<RT·code>systemd-logind</RT·code>) will aggressively terminate the background FUSE processes upon logout, destroying the management layer.
</p>
<p>
- The system setup process executes independently of user sessions. It queries a central SQLite database to determine the registered subu relationships, constructs the <RT-code>/home/master/subu</RT-code> directory structures, and establishes the ID-mapped mounts. Crucially, the setup process assigns ownership of these mounts to the master account. Because the mounts are created by a boot-level service, they persist indefinitely, surviving all individual user logouts.
+ The system setup process executes independently of user sessions. It queries a central SQLite database to determine the registered subu relationships, constructs the <RT·code>/home/master/subu</RT·code> directory structures, and establishes the ID-mapped mounts. Crucially, the setup process assigns ownership of these mounts to the master account. Because the mounts are created by a boot-level service, they persist indefinitely, surviving all individual user logouts.
</p>
<h1>User Manual</h1>
<h3>For the Subu</h3>
<ul>
- <li><strong>Home Directory:</strong> <RT-code>/home/Thomas_developer</RT-code></li>
+ <li><strong>Home Directory:</strong> <RT·code>/home/Thomas_developer</RT·code></li>
<li><strong>Perspective:</strong> The subu operates in a standard Linux environment. Files are owned by the subu. All paths behave normally.</li>
</ul>
<h3>For the Master User</h3>
<ul>
- <li><strong>Primary Workspace:</strong> <RT-code>/home/Thomas</RT-code></li>
- <li><strong>Subu Management Directory:</strong> <RT-code>/home/Thomas/subu/developer</RT-code></li>
- <li><strong>Perspective:</strong> The master user navigates into the <RT-code>subu</RT-code> directory to administer subordinate accounts. All files within this directory appear to be owned by the master user. Modifying a file here instantly modifies the physical file for the subu.</li>
+ <li><strong>Primary Workspace:</strong> <RT·code>/home/Thomas</RT·code></li>
+ <li><strong>Subu Management Directory:</strong> <RT·code>/home/Thomas/subu/developer</RT·code></li>
+ <li><strong>Perspective:</strong> The master user navigates into the <RT·code>subu</RT·code> directory to administer subordinate accounts. All files within this directory appear to be owned by the master user. Modifying a file here instantly modifies the physical file for the subu.</li>
</ul>
<h2>Backups</h2>
<p>
- When a person initiates a backup of their home directory, they must instruct the archiving tool (such as <RT-code>tar</RT-code> or <RT-code>rsync</RT-code>) to respect filesystem boundaries. Passing the <RT-code>--one-file-system</RT-code> flag prevents the backup utility from traversing into the <RT-code>/home/Thomas/subu</RT-code> mounts, avoiding infinite recursion loops and duplicate data capture. The physical subu directories are backed up via separate, independent archive routines.
+ When a person initiates a backup of their home directory, they must instruct the archiving tool (such as <RT·code>tar</RT·code> or <RT·code>rsync</RT·code>) to respect filesystem boundaries. Passing the <RT·code>--one-file-system</RT·code> flag prevents the backup utility from traversing into the <RT·code>/home/Thomas/subu</RT·code> mounts, avoiding infinite recursion loops and duplicate data capture. The physical subu directories are backed up via separate, independent archive routines.
</p>
- <script src="style/style_orchestrator.js"></script>
- <script>
- window.StyleRT.style_orchestrator();
- </script>
- </RT-article>
+ </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).
-*/
-window.StyleRT = window.StyleRT || {};
-
-window.StyleRT.RT_TOC = function() {
- const debug = window.StyleRT.debug || { log: function(){} };
- const toc_tags = document.querySelectorAll('rt-toc');
-
- toc_tags.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('RT_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('RT_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, it's a Main TOC. Otherwise it's 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 RT_code() {
- const RT = window.StyleRT;
- const U = RT.utility;
- const debug = RT.debug;
-
- debug.log('RT_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('RT_code', 'Render cycle complete.');
-}
-
-window.StyleRT = window.StyleRT || {};
-window.StyleRT.RT_code = RT_code;
+++ /dev/null
-/*
- Processes <RT-MATH> tags.
- JavaScript: RT_math()
- HTML Tag: <RT-MATH> (parsed as rt-math)
-*/
-function RT_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.RT_math = RT_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.RT_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', `RT_term failed: ${e && e.message ? e.message : String(e)}`);
- }
-};
+++ /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.RT_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('RT_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
-/* Style: The "State Department" Override
- Description: Restores decorum.
-*/
-(function(){
- const RT = window.StyleRT || {};
-
- // Force the font regardless of other settings
- RT.rubio = function() {
- const articles = document.querySelectorAll("rt-article");
- articles.forEach(el => {
- el.style.fontFamily = '"Times New Roman", "Times", serif';
- el.style.letterSpacing = "0px"; // No modern spacing allowed
- });
- };
-})();
+++ /dev/null
-/*
- Article Layout: Technical Reference
- Standard: Theme 1.0
- Description: High-readability layout for technical documentation on screens.
- Features: Sans-serif, justified text, distinct headers, boxed code.
-*/
-(function(){
- const RT = window.StyleRT = window.StyleRT || {};
-
- RT.article = function() {
- const debug = RT.debug || { log: function(){} };
- debug.log('layout', 'RT.article starting...');
-
- RT.config = RT.config || {};
-
- // Default Configuration
- RT.config.article = {
- font_family: '"Noto Sans", "Segoe UI", "Helvetica Neue", sans-serif'
- ,line_height: "1.8"
- ,font_size: "16px"
- ,font_weight: "400" // Default (String)
- ,max_width: "820px"
- ,margin: "0 auto"
- };
-
- // SAFE THEME DETECTION
- // If the theme is loaded and explicitly Light, bump the weight.
- try {
- if (RT.config.theme && RT.config.theme.meta_is_dark === false) {
- RT.config.article.font_weight = "600";
- debug.log('layout', 'Light theme detected: adjusting font weight to 600.');
- }
- } catch(e) {
- console.warn("StyleRT: Auto-weight adjustment failed, using default.", e);
- }
-
- const conf = RT.config.article;
- const article_seq = document.querySelectorAll("RT-article");
-
- if(article_seq.length === 0) {
- debug.log('layout', 'No <RT-article> elements found. Exiting.');
- return;
- }
-
- // 1. Apply Container Styles
- article_seq.forEach( (article) =>{
- const style = article.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)";
- });
-
- // 2. Inject Child Typography
- const style_id = 'rt-article-typography';
- if (!document.getElementById(style_id)) {
- debug.log('layout', 'Injecting CSS typography rules.');
- const style_el = document.createElement('style');
- style_el.id = style_id;
-
- style_el.textContent = `
- /* --- HEADERS --- */
- rt-article h1 {
- color: var(--rt-brand-primary);
- font-size: 2.0em;
- font-weight: 500;
- text-align: center;
- margin-top: 1.2em;
- margin-bottom: 0.6em;
- border-bottom: 2px solid var(--rt-border-default);
- padding-bottom: 0.3em;
- line-height: 1.2;
- letter-spacing: -0.02em;
- }
-
- rt-article h2 {
- color: var(--rt-brand-secondary);
- font-size: 1.5em;
- font-weight: 400;
- text-align: center;
- margin-top: 1.0em;
- margin-bottom: 0.5em;
- }
-
- rt-article h2 + h3 {
- margin-top: -0.3em;
- padding-top: 0;
- }
-
- rt-article h3 {
- color: var(--rt-brand-tertiary);
- font-size: 1.4em;
- font-weight: 400;
- margin-top: 1.0em;
- margin-bottom: 0.5em;
- }
-
- /* --- DEEP LEVELS (H4-H6) --- */
- rt-article h4, rt-article h5, rt-article h6 {
- color: var(--rt-brand-tertiary);
- font-weight: bold;
- margin-top: 1.2em;
- font-style: italic;
- }
- rt-article h4 { margin-left: 2em; }
- rt-article h5 { margin-left: 4em; }
- rt-article h6 { margin-left: 6em; }
-
- /* --- BODY TEXT --- */
- rt-article p {
- margin-bottom: 1.4em;
- text-align: justify;
- hyphens: auto;
- color: var(--rt-content-main);
- }
-
- /* --- RICH ELEMENTS --- */
- rt-article blockquote {
- border-left: 4px solid var(--rt-brand-secondary);
- margin: 1.5em 0;
- padding: 0.5em 1em;
- font-style: italic;
- color: var(--rt-content-muted);
- background: var(--rt-surface-1);
- border-radius: 0 4px 4px 0;
- }
-
- rt-article ul, rt-article ol {
- margin-bottom: 1.4em;
- padding-left: 2em;
- }
- rt-article li {
- margin-bottom: 0.4em;
- }
- rt-article li::marker {
- color: var(--rt-brand-secondary);
- font-weight: bold;
- }
-
- /* Links */
- rt-article a {
- color: var(--rt-brand-link);
- text-decoration: none;
- border-bottom: 1px dotted var(--rt-border-default);
- transition: all 0.2s;
- }
- rt-article a:hover {
- color: var(--rt-brand-primary);
- border-bottom: 1px solid var(--rt-brand-primary);
- background: var(--rt-surface-1);
- }
-
- /* --- TECHNICAL --- */
- rt-article pre {
- background: var(--rt-surface-code);
- padding: 1em;
- border-radius: 4px;
- overflow-x: auto;
- border: 1px solid var(--rt-border-default);
- }
- `;
- document.head.appendChild(style_el);
- }
- };
-})();
+++ /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
- <RT-article>: The root container.
- <RT-page>: The page wrapper.
-
-<RT-math>
-<RT-code>
-
- <RT-term>: For Conventional terms (standard definitions)
-<RT-neologism>: Terms introduced in this article
+++ /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
-/*
- Master Loader & Orchestrator for StyleRT.
-*/
-
-window.StyleRT = window.StyleRT || {};
-
-window.StyleRT.style_orchestrator = function() {
- const RT = window.StyleRT;
-
- const modules = [
- // Theme & Semantics
- 'style/RT_title.js',
- 'style/theme_dark_gold.js',
- 'style/RT_term.js',
- 'style/RT_math.js',
- 'style/RT_code.js',
- 'style/article_tech_ref.js',
- 'style/RT_TOC.js',
-
- // Layout & Pagination
- 'style/paginate_by_element.js',
- 'style/page_fixed_glow.js',
-
- // Visibility
- 'style/body_visibility_visible.js'
- ];
-
- // 1. Bootloader
- const utility = document.createElement('script');
- utility.src = 'style/utility.js';
-
- utility.onload = () => { load_next(0); };
- utility.onerror = () => { console.error("StyleRT: Critical failure - utility.js missing."); };
- document.head.appendChild(utility);
-
- // 2. The Chain Loader
- const load_next = (index) => {
- if (index >= modules.length) {
- run_style();
- return;
- }
- const src = modules[index];
- if (RT.debug) RT.debug.log('style', `Loading: ${src}`);
-
- const script = document.createElement('script');
- script.src = src;
- script.onload = () => load_next(index + 1);
- script.onerror = () => {
- console.error(`StyleRT: Failed load on ${src}`);
- load_next(index + 1);
- };
- document.head.appendChild(script);
- };
-
- // 3. Phase 1: Semantics
- const run_style = () => {
- RT.debug.log('style', 'Starting Phase 1: Setup & Semantics');
-
- // Naming Convention: RT.<filename_without_js>
- if(RT.theme) RT.theme();
- if(RT.article) RT.article();
-
- // NEW: Trigger the Title Generator
- if(RT.RT_title) RT.RT_title();
-
- if(RT.RT_term) RT.RT_term();
- if(RT.RT_math) RT.RT_math();
- if(RT.RT_code) RT.RT_code();
-
- if (window.MathJax && MathJax.Hub && MathJax.Hub.Queue) {
- RT.debug.log('style', 'MathJax detected. Queueing layout tasks...');
- MathJax.Hub.Queue(["Typeset", MathJax.Hub], continue_style);
- } else {
- continue_style();
- }
- };
-
- // 4. Phase 2: Layout
- const continue_style = () => {
- RT.debug.log('style', 'Starting Phase 2: Layout & Reveal');
-
- // Debug: Dump the config to see what values we are using
- if(RT.debug) RT.debug.log('config', JSON.stringify(RT.config || {}, null, 2));
-
- if(RT.RT_TOC) RT.RT_TOC();
- if(RT.paginate_by_element) RT.paginate_by_element();
- if(RT.page) RT.page(); // Defined in page_css_pn.js
- if(RT.body_visibility_visible) RT.body_visibility_visible();
-
- RT.debug.log('style', 'Style execution complete.');
- };
-};
+++ /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
-/*
- 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');
- }
-};