towishy_Owen-Graphite/dev/scripts/bundle_v3.py

142 lines
5.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
"""Bundle the v3 src/ tree into dist/theme-v3.css by following the @import
graph rooted at src/entry.css.
Design notes:
v3-rewrite [S3.1]: cascade pivot (no @layer) + base modules + v3 fingerprint baseline Major architectural decision: v3 drops @layer for theme content. CSS Cascade Layers spec says unlayered styles always defeat layered styles, and Obsidian core CSS (app.css) is unlayered. Wrapping the theme in @layer would require !important to win against core, defeating the v3 goal of reducing !important usage. Therefore v3 theme rules stay UNLAYERED and rely on (1) file @import order, (2) selector specificity, (3) remaining !important only where Obsidian core itself uses !important. Updated docs/v3/design-spec.md: replaced the @layer cascade section with a Cascade Strategy section explaining file-order + specificity. Added a decision log entry for the pivot and a S11 polish note clarifying that !important reduction is deferred from S3 to S11 (strangler pattern: correctness first via fingerprint diff = 0, then audit-driven reduction). Updated src/entry.css: removed layer() annotations from @import statements; expanded the file header to document the cascade strategy decision. Tokens import remains first, base modules now active, surfaces/chrome/etc. stay commented placeholders for S4+. Updated scripts/bundle_v3.py: removed @layer wrapping logic. Bundler now emits a plain depth-first concatenation of @import targets. Resulting dist/theme-v3.css is 1223 lines (up from 455 at S2) with !important=332 (mirrors dev/02 + dev/03 sources exactly, will reduce in S11 polish). Added src/base/10-base-workspace.css - copied verbatim from dev/02-base-workspace.css (118 lines, 14 !important). Provides workspace pane backgrounds, ribbon/tab colors, nav-folder/nav-file title colors, button surfaces, readable-line-width sizing, .markdown-rendered base typography (font-family, font-size from --ogd-body-size token, word-break). Added src/base/12-reading-content.css - copied verbatim from dev/03-reading-content.css (567 lines, 313 !important). Provides reading view editorial number kicker h1-h4, h2 gradient underline pill, base table structure (border-collapse, th/td padding, first-column accent), blockquote padding strip-back. Updated dev/v3/golden-rig/obsidian-harness.html: added a URL parameter switcher (?build=v3) so capture scripts can toggle between theme.css (v2 baseline) and dist/theme-v3.css (v3 candidate) without editing the file. Default remains theme.css. Updated scripts/capture_computed_fingerprint.py: added --build {v2,v3} flag. v3 output goes to docs/v3/computed-fingerprint-v<version>-v3-<theme>.json. Records the build value inside the JSON for traceability. Added scripts/fp_diff_summary.py - prints a per-element diff count between baseline and candidate fingerprints. Used to track S3-S6 closure progress against the 949 starting diff. Added scripts/fp_diff_inspect.py - prints every property diff for a single fingerprint element. Used to locate specific computed-style drift when the per-element count is high. Light fingerprint baseline gap measurement: pre-S3 949 mismatches (v3 was only tokens). After S3.1 base modules activated: 616 mismatches (35 percent closure). Remaining 616 are expected — h1 glassy surface comes from dev/10b + 10d (S5 surfaces / S6 chrome), nav/overlay/popover from S6, callout/table from S5, cm-* from S4 live preview. Captured docs/v3/computed-fingerprint-v2.30.14-v3-{light,dark}.json baselines for the v3 build after S3.1. validate_theme.py: still passes against v2.30.14 (theme.css unchanged at 16190 lines / 5816 !important / 37 Style Settings).
2026-05-16 02:15:28 +00:00
- @import statements are inlined verbatim. v3 deliberately does NOT wrap
imports in @layer because unlayered styles always beat layered ones, and
Obsidian core CSS is unlayered. See src/entry.css for the full rationale.
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
- Re-entrant imports are detected and refused.
- The canonical bundle is dist/theme-v3.css; theme.css at the repo root is a
copy promoted by dev/scripts/build_release.py or dev/scripts/sync_obsidian_theme.py.
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
ENTRY = ROOT / "src" / "entry.css"
DIST = ROOT / "dist" / "theme-v3.css"
IMPORT_RE = re.compile(
v3-rewrite [S3.1]: cascade pivot (no @layer) + base modules + v3 fingerprint baseline Major architectural decision: v3 drops @layer for theme content. CSS Cascade Layers spec says unlayered styles always defeat layered styles, and Obsidian core CSS (app.css) is unlayered. Wrapping the theme in @layer would require !important to win against core, defeating the v3 goal of reducing !important usage. Therefore v3 theme rules stay UNLAYERED and rely on (1) file @import order, (2) selector specificity, (3) remaining !important only where Obsidian core itself uses !important. Updated docs/v3/design-spec.md: replaced the @layer cascade section with a Cascade Strategy section explaining file-order + specificity. Added a decision log entry for the pivot and a S11 polish note clarifying that !important reduction is deferred from S3 to S11 (strangler pattern: correctness first via fingerprint diff = 0, then audit-driven reduction). Updated src/entry.css: removed layer() annotations from @import statements; expanded the file header to document the cascade strategy decision. Tokens import remains first, base modules now active, surfaces/chrome/etc. stay commented placeholders for S4+. Updated scripts/bundle_v3.py: removed @layer wrapping logic. Bundler now emits a plain depth-first concatenation of @import targets. Resulting dist/theme-v3.css is 1223 lines (up from 455 at S2) with !important=332 (mirrors dev/02 + dev/03 sources exactly, will reduce in S11 polish). Added src/base/10-base-workspace.css - copied verbatim from dev/02-base-workspace.css (118 lines, 14 !important). Provides workspace pane backgrounds, ribbon/tab colors, nav-folder/nav-file title colors, button surfaces, readable-line-width sizing, .markdown-rendered base typography (font-family, font-size from --ogd-body-size token, word-break). Added src/base/12-reading-content.css - copied verbatim from dev/03-reading-content.css (567 lines, 313 !important). Provides reading view editorial number kicker h1-h4, h2 gradient underline pill, base table structure (border-collapse, th/td padding, first-column accent), blockquote padding strip-back. Updated dev/v3/golden-rig/obsidian-harness.html: added a URL parameter switcher (?build=v3) so capture scripts can toggle between theme.css (v2 baseline) and dist/theme-v3.css (v3 candidate) without editing the file. Default remains theme.css. Updated scripts/capture_computed_fingerprint.py: added --build {v2,v3} flag. v3 output goes to docs/v3/computed-fingerprint-v<version>-v3-<theme>.json. Records the build value inside the JSON for traceability. Added scripts/fp_diff_summary.py - prints a per-element diff count between baseline and candidate fingerprints. Used to track S3-S6 closure progress against the 949 starting diff. Added scripts/fp_diff_inspect.py - prints every property diff for a single fingerprint element. Used to locate specific computed-style drift when the per-element count is high. Light fingerprint baseline gap measurement: pre-S3 949 mismatches (v3 was only tokens). After S3.1 base modules activated: 616 mismatches (35 percent closure). Remaining 616 are expected — h1 glassy surface comes from dev/10b + 10d (S5 surfaces / S6 chrome), nav/overlay/popover from S6, callout/table from S5, cm-* from S4 live preview. Captured docs/v3/computed-fingerprint-v2.30.14-v3-{light,dark}.json baselines for the v3 build after S3.1. validate_theme.py: still passes against v2.30.14 (theme.css unchanged at 16190 lines / 5816 !important / 37 Style Settings).
2026-05-16 02:15:28 +00:00
r"""@import\s+url\(\s*['"]?([^'")]+)['"]?\s*\)\s*;""",
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
re.IGNORECASE,
)
def is_commented_out(line: str) -> bool:
stripped = line.strip()
return stripped.startswith("/*") or stripped.startswith("//")
def resolve_imports(file_path: Path, visited: set[Path]) -> str:
real = file_path.resolve()
if real in visited:
raise RuntimeError(f"circular @import detected: {real}")
visited = visited | {real}
text = real.read_text(encoding="utf-8")
out_lines: list[str] = []
for raw_line in text.splitlines():
# Only treat a line as an @import directive if the directive is at the
v3-rewrite [S3.1]: cascade pivot (no @layer) + base modules + v3 fingerprint baseline Major architectural decision: v3 drops @layer for theme content. CSS Cascade Layers spec says unlayered styles always defeat layered styles, and Obsidian core CSS (app.css) is unlayered. Wrapping the theme in @layer would require !important to win against core, defeating the v3 goal of reducing !important usage. Therefore v3 theme rules stay UNLAYERED and rely on (1) file @import order, (2) selector specificity, (3) remaining !important only where Obsidian core itself uses !important. Updated docs/v3/design-spec.md: replaced the @layer cascade section with a Cascade Strategy section explaining file-order + specificity. Added a decision log entry for the pivot and a S11 polish note clarifying that !important reduction is deferred from S3 to S11 (strangler pattern: correctness first via fingerprint diff = 0, then audit-driven reduction). Updated src/entry.css: removed layer() annotations from @import statements; expanded the file header to document the cascade strategy decision. Tokens import remains first, base modules now active, surfaces/chrome/etc. stay commented placeholders for S4+. Updated scripts/bundle_v3.py: removed @layer wrapping logic. Bundler now emits a plain depth-first concatenation of @import targets. Resulting dist/theme-v3.css is 1223 lines (up from 455 at S2) with !important=332 (mirrors dev/02 + dev/03 sources exactly, will reduce in S11 polish). Added src/base/10-base-workspace.css - copied verbatim from dev/02-base-workspace.css (118 lines, 14 !important). Provides workspace pane backgrounds, ribbon/tab colors, nav-folder/nav-file title colors, button surfaces, readable-line-width sizing, .markdown-rendered base typography (font-family, font-size from --ogd-body-size token, word-break). Added src/base/12-reading-content.css - copied verbatim from dev/03-reading-content.css (567 lines, 313 !important). Provides reading view editorial number kicker h1-h4, h2 gradient underline pill, base table structure (border-collapse, th/td padding, first-column accent), blockquote padding strip-back. Updated dev/v3/golden-rig/obsidian-harness.html: added a URL parameter switcher (?build=v3) so capture scripts can toggle between theme.css (v2 baseline) and dist/theme-v3.css (v3 candidate) without editing the file. Default remains theme.css. Updated scripts/capture_computed_fingerprint.py: added --build {v2,v3} flag. v3 output goes to docs/v3/computed-fingerprint-v<version>-v3-<theme>.json. Records the build value inside the JSON for traceability. Added scripts/fp_diff_summary.py - prints a per-element diff count between baseline and candidate fingerprints. Used to track S3-S6 closure progress against the 949 starting diff. Added scripts/fp_diff_inspect.py - prints every property diff for a single fingerprint element. Used to locate specific computed-style drift when the per-element count is high. Light fingerprint baseline gap measurement: pre-S3 949 mismatches (v3 was only tokens). After S3.1 base modules activated: 616 mismatches (35 percent closure). Remaining 616 are expected — h1 glassy surface comes from dev/10b + 10d (S5 surfaces / S6 chrome), nav/overlay/popover from S6, callout/table from S5, cm-* from S4 live preview. Captured docs/v3/computed-fingerprint-v2.30.14-v3-{light,dark}.json baselines for the v3 build after S3.1. validate_theme.py: still passes against v2.30.14 (theme.css unchanged at 16190 lines / 5816 !important / 37 Style Settings).
2026-05-16 02:15:28 +00:00
# start of the line (ignoring whitespace) AND the line is not inside a
# comment. Commented-out @import lines (placeholders for future steps)
# must be passed through verbatim.
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
if "@import" not in raw_line:
out_lines.append(raw_line)
continue
if is_commented_out(raw_line):
out_lines.append(raw_line)
continue
m = IMPORT_RE.search(raw_line)
if not m:
out_lines.append(raw_line)
continue
rel = m.group(1)
target = (real.parent / rel).resolve()
if not target.is_file():
raise FileNotFoundError(f"@import target not found: {rel} (from {real.relative_to(ROOT)})")
inner = resolve_imports(target, visited)
rel_display = target.relative_to(ROOT).as_posix()
v3-rewrite [S3.1]: cascade pivot (no @layer) + base modules + v3 fingerprint baseline Major architectural decision: v3 drops @layer for theme content. CSS Cascade Layers spec says unlayered styles always defeat layered styles, and Obsidian core CSS (app.css) is unlayered. Wrapping the theme in @layer would require !important to win against core, defeating the v3 goal of reducing !important usage. Therefore v3 theme rules stay UNLAYERED and rely on (1) file @import order, (2) selector specificity, (3) remaining !important only where Obsidian core itself uses !important. Updated docs/v3/design-spec.md: replaced the @layer cascade section with a Cascade Strategy section explaining file-order + specificity. Added a decision log entry for the pivot and a S11 polish note clarifying that !important reduction is deferred from S3 to S11 (strangler pattern: correctness first via fingerprint diff = 0, then audit-driven reduction). Updated src/entry.css: removed layer() annotations from @import statements; expanded the file header to document the cascade strategy decision. Tokens import remains first, base modules now active, surfaces/chrome/etc. stay commented placeholders for S4+. Updated scripts/bundle_v3.py: removed @layer wrapping logic. Bundler now emits a plain depth-first concatenation of @import targets. Resulting dist/theme-v3.css is 1223 lines (up from 455 at S2) with !important=332 (mirrors dev/02 + dev/03 sources exactly, will reduce in S11 polish). Added src/base/10-base-workspace.css - copied verbatim from dev/02-base-workspace.css (118 lines, 14 !important). Provides workspace pane backgrounds, ribbon/tab colors, nav-folder/nav-file title colors, button surfaces, readable-line-width sizing, .markdown-rendered base typography (font-family, font-size from --ogd-body-size token, word-break). Added src/base/12-reading-content.css - copied verbatim from dev/03-reading-content.css (567 lines, 313 !important). Provides reading view editorial number kicker h1-h4, h2 gradient underline pill, base table structure (border-collapse, th/td padding, first-column accent), blockquote padding strip-back. Updated dev/v3/golden-rig/obsidian-harness.html: added a URL parameter switcher (?build=v3) so capture scripts can toggle between theme.css (v2 baseline) and dist/theme-v3.css (v3 candidate) without editing the file. Default remains theme.css. Updated scripts/capture_computed_fingerprint.py: added --build {v2,v3} flag. v3 output goes to docs/v3/computed-fingerprint-v<version>-v3-<theme>.json. Records the build value inside the JSON for traceability. Added scripts/fp_diff_summary.py - prints a per-element diff count between baseline and candidate fingerprints. Used to track S3-S6 closure progress against the 949 starting diff. Added scripts/fp_diff_inspect.py - prints every property diff for a single fingerprint element. Used to locate specific computed-style drift when the per-element count is high. Light fingerprint baseline gap measurement: pre-S3 949 mismatches (v3 was only tokens). After S3.1 base modules activated: 616 mismatches (35 percent closure). Remaining 616 are expected — h1 glassy surface comes from dev/10b + 10d (S5 surfaces / S6 chrome), nav/overlay/popover from S6, callout/table from S5, cm-* from S4 live preview. Captured docs/v3/computed-fingerprint-v2.30.14-v3-{light,dark}.json baselines for the v3 build after S3.1. validate_theme.py: still passes against v2.30.14 (theme.css unchanged at 16190 lines / 5816 !important / 37 Style Settings).
2026-05-16 02:15:28 +00:00
out_lines.append(f"/* >>> {rel_display} */")
out_lines.append(inner)
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
out_lines.append(f"/* <<< {rel_display} */")
return "\n".join(out_lines)
def bundle(entry: Path, dist: Path, dedup: bool = True, check: bool = False) -> int:
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
if not entry.is_file():
print(f"ERROR: entry not found: {entry}")
return 1
bundled = resolve_imports(entry, set())
dist.parent.mkdir(parents=True, exist_ok=True)
header = (
"/* Owen Graphite v3 — bundled theme stylesheet.\n"
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
" *\n"
" * Generated by dev/scripts/bundle_v3.py from src/entry.css.\n"
" * Do not hand-edit. The @import / cascade order is defined in src/entry.css.\n"
" * Same-context duplicate-selector dedup pass: dev/scripts/dedup_v3.py.\n"
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
" *\n"
" * The repo root theme.css is a promoted copy of this bundle; see\n"
" * dev/scripts/build_release.py and dev/scripts/sync_obsidian_theme.py.\n"
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
" */\n\n"
)
merges = 0
if dedup:
# Import lazily so bundle_v3.py remains importable even if dedup_v3
# is removed/relocated.
import importlib.util
dedup_path = ROOT / "dev" / "scripts" / "dedup_v3.py"
spec = importlib.util.spec_from_file_location("dedup_v3", dedup_path)
if spec is None or spec.loader is None:
raise RuntimeError("unable to load dev/scripts/dedup_v3.py")
dedup_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(dedup_mod)
bundled, merges = dedup_mod.dedup_scope(bundled)
2026-05-17 03:26:21 +00:00
bundled = "\n".join(line.rstrip() for line in bundled.splitlines())
new_content = header + bundled + "\n"
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
line_count = bundled.count("\n") + 1
bang_count = bundled.count("!important")
if check:
# Determinism / freshness gate: compare against existing dist file.
if not dist.is_file():
print(f"FAIL: {dist.relative_to(ROOT)} missing; run bundle_v3.py to generate.")
return 1
current = dist.read_text(encoding="utf-8")
if current != new_content:
print(
f"FAIL: {dist.relative_to(ROOT)} is stale vs src/.\n"
f" Re-run: python dev/scripts/bundle_v3.py"
)
return 1
print(
f"OK: {dist.relative_to(ROOT)} matches src/ "
f"({line_count} lines, !important={bang_count}, dedup_merges={merges})"
)
return 0
dist.write_text(new_content, encoding="utf-8")
print(
f"OK: bundled {dist.relative_to(ROOT)} "
f"({line_count} lines, !important={bang_count}, dedup_merges={merges})"
)
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
return 0
def main(argv: list[str] | None = None) -> int:
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--entry", type=Path, default=ENTRY)
parser.add_argument("--out", type=Path, default=DIST)
parser.add_argument("--no-dedup", action="store_true",
help="Skip the post-bundle same-context selector dedup pass.")
parser.add_argument("--check", action="store_true",
help="Verify that dist matches a fresh bundle from src/; do not write.")
args = parser.parse_args(argv)
return bundle(args.entry, args.out, dedup=not args.no_dedup, check=args.check)
v3-rewrite [S2]: src/ scaffold + entry.css + tokens layer + bundle_v3.py + token contract verifier S2 completes the v3 build infrastructure. The cascade layer order is declared in src/entry.css and the tokens layer is populated with v2.30.14-equivalent light and dark global defaults. Verifier confirms the token contract holds (57 light + 72 dark = 129 unique --ogd-* global defaults match dev/* exactly). Added src/entry.css - declares @layer reset, tokens, base, surfaces, chrome, features, plugins, dark, a11y, hotfix and @imports the tokens modules with explicit layer() annotations. Subsequent steps (S3 onward) uncomment @import lines as their modules ship. Added src/tokens/00-light-tokens.css - dev/01-tokens.css carried over verbatim; provides :root --ogd-* defaults plus the .theme-light, .theme-dark shared Obsidian-core variable block. 179 lines, untouched values. Added src/tokens/01-dark-tokens.css - auto-generated by scripts/generate_v3_dark_tokens.py. Contains 72 global dark --ogd-* tokens and 54 core variable overrides across 7 selector groups (.theme-dark, body.theme-dark, body:not(.is-mobile).theme-dark, body:not(.is-mobile).theme-dark.ogd-glass-{reduced,strong,subtle}). Descendant-context dark overrides (105 callout/table/code/heading tokens) stay in dev/ for now; they migrate into surfaces/ and base/ in S3-S5. Added scripts/generate_v3_dark_tokens.py - selector-aware extractor: walks dev/*.css in _order.txt order, harvests --ogd-* and whitelisted core variable declarations from any non-descendant selector mentioning .theme-dark, groups by selector, writes src/tokens/01-dark-tokens.css. Rerun after changing dev/. Added scripts/bundle_v3.py - depth-first @import resolver rooted at src/entry.css. Wraps each imported file in @layer name when the @import directive carries layer(name). Generates dist/theme-v3.css (443 lines, !important=0). dist/ stays gitignored; theme.css remains the v2.30.14 build until S11 swap. Added scripts/verify_v3_token_contract.py - applies the same global-selector rule (:root for light, non-descendant .theme-dark for dark) to both dev/ and src/tokens/, then diffs. Run as the pre-commit guard for any tokens-layer change in v3-rewrite. Passes on the S2 baseline: 57 light + 72 dark defaults match. validate_theme.py still passes against v2.30.14 (16190 lines / 5816 !important / 37 Style Settings). theme.css unchanged. No regression risk for shipped releases.
2026-05-16 02:04:23 +00:00
if __name__ == "__main__":
raise SystemExit(main())