From e69dae6b482d7fdaaacbed99e6262b44b5689323 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Fri, 9 Oct 2026 08:51:53 +0000 Subject: [PATCH] RT-Style v6.0 --- .../document/RT-Manuscript_locator.js | 51 ---- administrator/document/how-to_release.html | 10 +- administrator/tool/RT-Style_header_v5_to_v6 | 282 ++++++++++++++++++ .../tool/deploy_RT-Style_indirect_locators | 66 ---- developer/document/RT-Manuscript_locator.js | 51 ---- .../document/naming_file-and-directory.html | 10 +- .../single-file_C-module-and-namespace.html | 10 +- document/RT-Manuscript_locator.js | 51 ---- document/introduction_Harmony.html | 10 +- ...role-and-workflow_product-development.html | 10 +- ...role-and-workflow_product-maintenance.html | 10 +- shared/document/RT-Manuscript_locator.js | 54 ---- 12 files changed, 336 insertions(+), 279 deletions(-) delete mode 100644 administrator/document/RT-Manuscript_locator.js create mode 100755 administrator/tool/RT-Style_header_v5_to_v6 delete mode 100755 administrator/tool/deploy_RT-Style_indirect_locators delete mode 100644 developer/document/RT-Manuscript_locator.js delete mode 100644 document/RT-Manuscript_locator.js delete mode 100644 shared/document/RT-Manuscript_locator.js diff --git a/administrator/document/RT-Manuscript_locator.js b/administrator/document/RT-Manuscript_locator.js deleted file mode 100644 index 704752b..0000000 --- a/administrator/document/RT-Manuscript_locator.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - indirect.js - - We have four scenarios - - immediate - used in the RT-Style distribution itself (authored, consumer, staged) - direct - used in the RT-Style project itself, but not in the distribution - indirect - the version all Harmony projects use - URL-only - always pulls style through a URL, a webserver must be present - -*/ - -window.RT = window.RT || {}; - -// --- Configuration --- -// Define the consumer project name to allow dynamic local file:// calculation. -window.RT.project_name = "Harmony"; - -// Fallback URL when served over a network where the project root is not in the URI. -window.RT.server_url = "http://localhost:8000/shared/linked-project/RT-Style/Manuscript"; - -(function() { - let style_path = window.RT.server_url; - - if (window.RT.project_name) { - const path = window.location.pathname; - const project_root_index = path.indexOf('/' + window.RT.project_name + '/'); - - if (project_root_index !== -1) { - // substring(0, stop) extracts up to the project name, leaving off the trailing slash. - // We append the explicit forward slash before navigating into the shared boundary. - const absolute_project_root = path.substring(0, project_root_index + window.RT.project_name.length + 1); - - // The symlink 'RT-Style' already drops us inside the 'consumer/' directory, - // so we proceed directly to 'Manuscript'. - style_path = absolute_project_root + "/shared/linked-project/RT-Style/Manuscript"; - } else { - console.warn("RT-Style: Cannot locate project root '/" + window.RT.project_name + "/' in URI. Falling back to server_url."); - } - } - - window.RT.dirpr_library = style_path; - - document.write( - ' + + +A version 6 document carries an inline header block instead. The block takes +the library from the record made by opening a Manuscript directory's +setup.html, and falls back to the Reasoning Technology server. + +For each version 5 .html file found, the tool replaces the locator line in + with the version 6 block, keeping its indentation, and deletes +RT-Manuscript_locator.js from that file's directory. Any other mention of +RT-Manuscript_locator in the file (an escaped example, prose) is logged for a +person to edit. + +Usage: RT-Style_header_v5_to_v6 [file ...] + RT-Style_header_v5_to_v6 preview [file ...] + + help | -h | --help print this message (also printed when given no arguments) + project walk the directory tree rooted at $REPO_HOME + tree walk the directory tree rooted at the current directory + local only the current directory, no subdirectories + file ... only the named .html files + preview print the log of what would do, change nothing + +Every directory is walked, whatever its name. Symbolic links to directories +are not followed, so a link back up the tree cannot cause a loop or reach +outside it. +""" + +import sys ,os ,re +from pathlib import Path + +LOCATOR_FILE = "RT-Manuscript_locator.js" +V6_MARK = 'localStorage.getItem("RT.dirpr_library")' + +V6_BLOCK = [ + '' +] + +LOCATOR_LINE = re.compile( + r'^(?P[ \t]*)\s*\s*$' + ,re.IGNORECASE +) +HEAD_END = re.compile(r'' ,re.IGNORECASE) + + +# ---------------------------------------------------------------- log + +class Log: + def __init__(self ,preview: bool ,base: Path): + self.preview = preview + self.base = base + self.count = {"converted": 0 ,"deleted": 0 ,"already": 0 ,"hand": 0 + ,"untouched": 0 ,"stray": 0 ,"error": 0} + + def rel(self ,p: Path) -> str: + try: return str(p.resolve().relative_to(self.base)) + except ValueError: return str(p) + + def line(self ,tag: str ,text: str): + print(f" {tag:<15} {text}") + + def act(self ,done: str ,would: str ,p: Path ,key: str): + self.line(would if self.preview else done ,self.rel(p)) + self.count[key] += 1 + + def summary(self): + c = self.count + v = "would be " if self.preview else "" + print() + print("summary:") + print(f" {c['converted']:>4} .html files {v}converted to version 6") + print(f" {c['deleted']:>4} {LOCATOR_FILE} files {v}deleted") + print(f" {c['already']:>4} .html files already version 6") + print(f" {c['hand']:>4} .html files with mentions to edit by hand") + print(f" {c['untouched']:>4} other .html files left alone") + if c['stray']: + print(f" {c['stray']:>4} {LOCATOR_FILE} files kept, no version 5 document beside them") + if c['error']: + print(f" {c['error']:>4} errors") + if self.preview: + print("preview only: nothing was changed") + + +# ---------------------------------------------------------------- finding files + +def walk(root: Path ,recursive: bool): + """Yields (directory ,[.html files] ,has_locator) for each directory.""" + if not recursive: + entries = sorted(root.iterdir()) + yield root ,[e for e in entries if e.is_file() and e.suffix == ".html"] ,(root / LOCATOR_FILE).is_file() + return + for d ,dirs ,files in os.walk(root): + dirs.sort() + files = sorted(files) + yield Path(d) ,[Path(d) / f for f in files if f.endswith(".html")] ,LOCATOR_FILE in files + + +# ---------------------------------------------------------------- conversion + +def convert(text: str): + """Returns (new_text ,replaced_count ,leftover). leftover lists + (line_number ,line) for locator mentions that were not replaced.""" + lines = text.split("\n") + out = [] + replaced = 0 + leftover = [] + in_head = True + for n ,line in enumerate(lines ,1): + m = LOCATOR_LINE.match(line) if in_head else None + if m: + eol = "\r" if line.endswith("\r") else "" + out.extend(m.group("indent") + b + eol for b in V6_BLOCK) + replaced += 1 + else: + out.append(line) + if LOCATOR_FILE.removesuffix(".js") in line: + leftover.append((n ,line.strip())) + if in_head and HEAD_END.search(line): + in_head = False + return "\n".join(out) ,replaced ,leftover + +def do_file(f: Path ,log: Log) -> bool: + """Converts one .html file. Returns True when it was version 5.""" + try: + text = f.read_text(encoding="utf-8") + except Exception as e: + log.line("error" ,f"{log.rel(f)}: cannot read: {e}") + log.count["error"] += 1 + return False + + if V6_MARK in text and LOCATOR_FILE.removesuffix(".js") not in text: + log.line("already v6" ,log.rel(f)) + log.count["already"] += 1 + return False + + new_text ,replaced ,leftover = convert(text) + + if replaced: + if not log.preview: + try: + mode = f.stat().st_mode + f.write_text(new_text ,encoding="utf-8") + os.chmod(f ,mode) + except Exception as e: + log.line("error" ,f"{log.rel(f)}: cannot write: {e}") + log.count["error"] += 1 + return False + log.act("converted" ,"would convert" ,f ,"converted") + if replaced > 1: + log.line("note" ,f"{log.rel(f)}: {replaced} locator lines in , all replaced") + elif not leftover: + log.count["untouched"] += 1 + + if leftover: + log.count["hand"] += 1 + for n ,line in leftover: + log.line("edit by hand" ,f"{log.rel(f)}:{n}: {line[:100]}") + + return replaced > 0 + +def do_locator(d: Path ,log: Log): + loc = d / LOCATOR_FILE + if not log.preview: + try: + loc.unlink() + except Exception as e: + log.line("error" ,f"{log.rel(loc)}: cannot delete: {e}") + log.count["error"] += 1 + return + log.act("deleted" ,"would delete" ,loc ,"deleted") + +def run_dirs(root: Path ,recursive: bool ,log: Log): + for d ,html ,has_locator in walk(root ,recursive): + any_v5 = False + for f in html: + any_v5 = do_file(f ,log) or any_v5 + if has_locator: + if any_v5: + do_locator(d ,log) + else: + log.line("kept" ,f"{log.rel(d / LOCATOR_FILE)} (no version 5 document beside it)") + log.count["stray"] += 1 + +def run_files(files: list[Path] ,log: Log): + v5_dirs = [] + for f in files: + if do_file(f ,log): + d = f.parent.resolve() + if d not in v5_dirs: v5_dirs.append(d) + for d in v5_dirs: + if (d / LOCATOR_FILE).is_file(): + do_locator(d ,log) + + +# ---------------------------------------------------------------- command line + +def help() -> int: + print(__doc__.strip()) + return 0 + +def CLI(argv=None) -> int: + if argv is None: + argv = sys.argv[1:] + if not argv or argv[0] in ("help" ,"-h" ,"--help"): + return help() + + preview = False + if argv[0] == "preview": + preview = True + argv = argv[1:] + if not argv: + print("Error: 'preview' needs a command after it.\n" ,file=sys.stderr) + help() + return 1 + + command ,rest = argv[0] ,argv[1:] + + if command == "file": + if not rest: + print("Error: 'file' needs at least one file name." ,file=sys.stderr) + return 1 + files = [Path(a) for a in rest] + for f in files: + if not f.is_file(): + print(f"Error: {f} is not a file." ,file=sys.stderr) + return 1 + base = Path.cwd().resolve() + log = Log(preview ,base) + print(f"RT-Style_header_v5_to_v6 {'preview ' if preview else ''}file in {base}") + run_files(files ,log) + log.summary() + return 1 if log.count["error"] else 0 + + if rest: + print(f"Error: '{command}' takes no arguments." ,file=sys.stderr) + return 1 + + if command == "project": + repo_home = os.environ.get("REPO_HOME") + if not repo_home: + print("Error: 'project' needs REPO_HOME to be set." ,file=sys.stderr) + return 1 + root ,recursive = Path(repo_home) ,True + elif command == "tree": + root ,recursive = Path.cwd() ,True + elif command == "local": + root ,recursive = Path.cwd() ,False + else: + print(f"Error: unknown command '{command}'.\n" ,file=sys.stderr) + help() + return 1 + + if not root.is_dir(): + print(f"Error: {root} is not a directory." ,file=sys.stderr) + return 1 + + base = root.resolve() + log = Log(preview ,base) + print(f"RT-Style_header_v5_to_v6 {'preview ' if preview else ''}{command} in {base}") + run_dirs(base ,recursive ,log) + log.summary() + return 1 if log.count["error"] else 0 + +if __name__ == "__main__": + sys.exit(CLI()) diff --git a/administrator/tool/deploy_RT-Style_indirect_locators b/administrator/tool/deploy_RT-Style_indirect_locators deleted file mode 100755 index 13af7b0..0000000 --- a/administrator/tool/deploy_RT-Style_indirect_locators +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python3 -import sys -import os -import stat -import shutil -from pathlib import Path - -def deploy_RT_Manuscript_indirect_locators(locator_dir_path): - locator_dir = Path(locator_dir_path) - if not locator_dir.is_dir(): - print(f"Error: Locator directory not found at {locator_dir_path}") - sys.exit(1) - - indirect_js = locator_dir / "indirect.js" - if not indirect_js.exists(): - print("Error: indirect.js missing in the specified directory.") - sys.exit(1) - - repo_home = Path(os.environ.get("REPO_HOME", ".")) - IGNORED_DIRS = {".git"} - updated_dirs = set() - - for root, dirs, files in os.walk(repo_home): - # Prune ignored directories in place - dirs[:] = [d for d in dirs if d not in IGNORED_DIRS] - current_path = Path(root) - - # Skip if the directory has already received a locator during this run - if current_path in updated_dirs: - continue - - needs_locator = False - for file_name in files: - if file_name.endswith(".html"): - file_path = current_path / file_name - try: - with open(file_path, "r", encoding="utf-8") as f: - for line in f: - if "RT-Manuscript_locator.js" in line: - needs_locator = True - break - if "" in line.lower(): - break - if needs_locator: - break - except Exception as e: - print(f"Warning: Could not read {file_path}: {e}") - - if needs_locator: - target_file = current_path / "RT-Manuscript_locator.js" - - # Eliminate permission denial on read-only artifacts - if target_file.exists(): - target_file.chmod(target_file.stat().st_mode | stat.S_IWUSR) - - shutil.copyfile(indirect_js, target_file) - print(f"Copied indirect.js -> {target_file}") - - updated_dirs.add(current_path) - -if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: python3 deploy_RT_Manuscript_indirect_locators.py ") - sys.exit(1) - - deploy_RT_Manuscript_indirect_locators(sys.argv[1]) diff --git a/developer/document/RT-Manuscript_locator.js b/developer/document/RT-Manuscript_locator.js deleted file mode 100644 index 704752b..0000000 --- a/developer/document/RT-Manuscript_locator.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - indirect.js - - We have four scenarios - - immediate - used in the RT-Style distribution itself (authored, consumer, staged) - direct - used in the RT-Style project itself, but not in the distribution - indirect - the version all Harmony projects use - URL-only - always pulls style through a URL, a webserver must be present - -*/ - -window.RT = window.RT || {}; - -// --- Configuration --- -// Define the consumer project name to allow dynamic local file:// calculation. -window.RT.project_name = "Harmony"; - -// Fallback URL when served over a network where the project root is not in the URI. -window.RT.server_url = "http://localhost:8000/shared/linked-project/RT-Style/Manuscript"; - -(function() { - let style_path = window.RT.server_url; - - if (window.RT.project_name) { - const path = window.location.pathname; - const project_root_index = path.indexOf('/' + window.RT.project_name + '/'); - - if (project_root_index !== -1) { - // substring(0, stop) extracts up to the project name, leaving off the trailing slash. - // We append the explicit forward slash before navigating into the shared boundary. - const absolute_project_root = path.substring(0, project_root_index + window.RT.project_name.length + 1); - - // The symlink 'RT-Style' already drops us inside the 'consumer/' directory, - // so we proceed directly to 'Manuscript'. - style_path = absolute_project_root + "/shared/linked-project/RT-Style/Manuscript"; - } else { - console.warn("RT-Style: Cannot locate project root '/" + window.RT.project_name + "/' in URI. Falling back to server_url."); - } - } - - window.RT.dirpr_library = style_path; - - document.write( - ' + + + + +