Skip to content

feat: bake tweakcn themes into projects at build time#23

Merged
Shewart merged 5 commits into
mainfrom
feat/tweakcn-theme-import
Jul 3, 2026
Merged

feat: bake tweakcn themes into projects at build time#23
Shewart merged 5 commits into
mainfrom
feat/tweakcn-theme-import

Conversation

@Shewart

@Shewart Shewart commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Closes #21.

Adds shellui theme commands that fetch a theme from tweakcn.com and bake its CSS variables into the consumer's project at build time — no runtime fetch, works offline, exactly-what-you-see-is-what-ships.

What ships

shellui theme apply https://tweakcn.com/themes/<id>
shellui theme apply <url> --emit-override wwwroot/theme.css   # for Path A/D consumers
shellui theme update                                          # re-fetch from lock file
  • apply — fetches tweakcn's public /r/themes/<id> endpoint (no auth, CORS open, force-static), converts the payload into :root {}, .dark {}, and @theme inline {} blocks, and writes them into wwwroot/input.css between sentinel markers. Content outside the markers is preserved verbatim, so user's custom utilities, @source directives, and imports survive re-applies.
  • --emit-override <path> — writes only the theme block to a standalone CSS file. Path A/D consumers link this AFTER shellui-all.css in their <head> so overrides win the cascade.
  • update — reads shellui.theme.lock, re-fetches from the recorded URL, re-applies. Enables reproducible refreshes without the user having to remember the URL.
  • shellui.theme.lock — records SourceUrl, ContentSha256, ThemeName, AppliedAt. Small enough to commit.

URL normalization

Accepts every form users might paste:

Input Normalized to
https://tweakcn.com/themes/<id> https://tweakcn.com/r/themes/<id>
https://tweakcn.com/themes/<id>?tab=colors https://tweakcn.com/r/themes/<id>
https://tweakcn.com/r/themes/<id> (unchanged)
http://tweakcn.com/themes/<id> https://tweakcn.com/r/themes/<id>
<id> alone https://tweakcn.com/r/themes/<id>

Rejects non-tweakcn URLs, empty input, dashboard links.

Issue #21 scope — what's in / what's deferred

Item Status
shellui theme apply <url> — rewrite input.css :root/.dark blocks ✅ shipped
shellui theme apply <url> --emit-override <path> — Path A/D override CSS ✅ shipped
shellui theme update — refresh from last-used URL ✅ shipped
shellui.theme.lock — source URL + SHA-256 for reproducible re-applies ✅ shipped
shellui theme init <url> — combined init + apply chain ⏭️ follow-up
Extend the four install-path READMEs in shellui-installation-tests/ with theme-apply examples ⏭️ follow-up
New test project path-c-cli-tweakcn/ — end-to-end demo ⏭️ follow-up

Files

File Purpose
src/ShellUI.CLI/Services/ThemeService.cs Fetch, parse, CSS emit, sentinel-safe apply, lock file r/w
src/ShellUI.CLI/Program.cs Wires theme apply / theme update subcommands
src/ShellUI.Core/Models/ThemeLockFile.cs Data model for shellui.theme.lock
ShellUI.Tests/ThemeServiceTests.cs 20 unit tests
tools/ShellUI.SafelistGenerator/Program.cs Comment-noise cleanup only (essential WHYs retained)

Why not runtime fetch (recap from #21)

A ShellUI.applyTweakcnTheme(url) JS helper would:

  • Flash the wrong theme on first paint (FOUT)
  • Add a runtime dependency on tweakcn.com being up
  • Break offline / airgapped / CI environments
  • Cost an HTTP round-trip on every page load

Build-time bake avoids all four. If runtime demand shows up later, the helper can ship opt-in.

Test plan

  • dotnet test — 76/76 green (56 existing + 20 new ThemeService tests)
  • shellui theme apply https://tweakcn.com/themes/cmgy5f2vg000504l42pep9ob1 in path-c-cli — 53 light + 52 dark vars written to input.css between sentinels
  • Re-run same command — idempotent, byte-identical output
  • shellui theme update — re-fetches from lock, no URL argument needed
  • shellui theme apply <url> --emit-override wwwroot/theme.css — standalone override written, "next step" hint printed for Path A/D
  • User content added outside sentinels survives re-apply (covered by Apply_PreservesUserContentAboveAndBelow_WhenSentinelExists)

Shewart added 5 commits July 3, 2026 21:51
- Introduced new commands for theme management in the CLI, allowing users to fetch and apply themes from tweakcn.com.
- Added `theme apply` command to fetch a theme and update `wwwroot/input.css`, with options for standalone CSS file generation.
- Implemented `theme update` command to re-fetch and apply the previously recorded theme from `shellui.theme.lock`.
- Created a new `ThemeService` class to handle theme fetching, parsing, and application logic, enhancing modularity and maintainability.
- Simplified comments in `ShellUI.Components.targets` to maintain focus on the auto-generated nature of the file.
- Removed redundant explanations while retaining essential information about the safelist generation process.
- Updated comments in `Program.cs` to clarify the functionality of the safelist generator, including the types of files scanned and the output artifacts.
- Streamlined the code by removing redundant comments and improving clarity on the filtering process for class-list strings.
- Ensured consistency in the documentation style to align with recent changes in the project.
- Introduced a new `ThemeLockFile` class to record the applied theme details, including the source URL, content hash, theme name, and application timestamp.
- This model supports the theme management functionality by enabling the `shellui theme update` command to reference previously applied themes.
- Introduced a new test file `ThemeServiceTests.cs` to validate the functionality of the `ThemeService` class.
- Implemented tests for URL normalization, JSON parsing, CSS generation, and application to input CSS, ensuring robust coverage of theme management features.
- Added various test cases to handle accepted and rejected URL formats, verify JSON structure, and confirm correct CSS output.
Copilot AI review requested due to automatic review settings July 3, 2026 20:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Shewart Shewart merged commit d7ff0c2 into main Jul 3, 2026
1 check passed
@Shewart Shewart deleted the feat/tweakcn-theme-import branch July 3, 2026 20:04
Shewart added a commit that referenced this pull request Jul 3, 2026
Follow-up to #23 (tweakcn theme import). Closes the last three items on
issue #21.

## What ships

### 1. `shellui theme init <url>` — one-shot init + apply

Combined command for greenfield setup. Runs
`InitService.InitializeAsync` (Tailwind CLI download, `App.razor` patch,
`input.css`, `shellui.json`, MSBuild wiring) then chains into
`ApplyThemeAsync` — so a fresh project goes from empty to fully themed
in one command:

```bash
dotnet shellui theme init https://tweakcn.com/themes/<id> --yes
```

Accepts the same options as `init`: `--force`, `--style`, `--tailwind`,
`--yes`. Console output is two labelled steps so users can see which
phase they're in if something fails.

Fits alongside the existing `theme apply` (existing project) and `theme
update` (refresh from lock) — the full CLI surface is now:

```bash
shellui theme init <url>                                # fresh project — init + apply
shellui theme apply <url>                               # existing project — write to input.css
shellui theme apply <url> --emit-override <path>        # Path A/D — standalone override CSS
shellui theme update                                    # re-fetch from shellui.theme.lock
```

### 2. README coverage across all install-path docs

Three READMEs now cover the tweakcn workflow with concrete commands per
install path:

| README | Change |
|---|---|
| **Top-level `README.md`** | New "Using tweakcn themes with `shellui
theme`" section before "Theming across paths". Existing table extended
with an "Auto-import from tweakcn" column showing the exact command for
each path. |
| **`src/ShellUI.CLI/README.md`** | Quick Start shows the `theme init`
one-shot alternative alongside plain `init`. New `theme` commands
section covers all three subcommands + lock file semantics. |
| **`src/ShellUI.Components/README.md`** | Path A trade-off block now
shows `--emit-override` as the recommended override workflow. Path B
shows `theme apply` as the tweakcn auto-import path. Path C Quick Start
includes the `theme init` one-shot. Bottom "Theme Customization" section
rewritten to lead with the CLI, keep the manual paste as fallback. |

### 3. `path-c-cli-tweakcn/` demo — verified locally, not committed

The `shellui-installation-tests/` directory is gitignored (per its
README). Rather than land a fifth test project there that won't merge, I
verified the flow end-to-end in a scratchpad:

- `dotnet new blazor -n TestApp`
- `shellui theme init
https://tweakcn.com/themes/cmgy5f2vg000504l42pep9ob1 --yes`
- Step 1/2 (init) — Tailwind CLI downloaded, `App.razor` patched,
`input.css` + `app.css` + `tailwind.config.js` + `shellui.json` +
`Build/ShellUI.targets` created
- Step 2/2 (apply) — theme fetched (53 light + 52 dark vars = 128 oklch
values in output), sentinel-marked block written to `input.css`,
`shellui.theme.lock` written with correct SHA-256
- `dotnet build` succeeds; Tailwind rebuilds; components render with the
tweakcn theme

## Issue #21 status

All checkboxes now green:

| Item | PR | Status |
|---|---|---|
| `shellui theme apply <url>` | #23 | ✅ |
| `shellui theme apply <url> --emit-override <path>` | #23 | ✅ |
| `shellui theme update` | #23 | ✅ |
| `shellui.theme.lock` | #23 | ✅ |
| `shellui theme init <url>` | **this PR** | ✅ |
| Install-path READMEs with theme-apply examples | **this PR** | ✅
(three READMEs updated; per-path READMEs under
`shellui-installation-tests/` are gitignored, so the user-visible
surface is top-level + package READMEs) |
| Test project `path-c-cli-tweakcn/` — end-to-end demo | **this PR** | ✅
(verified locally; not committed since the parent dir is gitignored) |

## Files

| File | Change |
|---|---|
| `src/ShellUI.CLI/Program.cs` | +40 — `CreateThemeInitCommand` +
registration under `theme` |
| `README.md` | +47 −5 — new "Using tweakcn themes" section + expanded
theming table |
| `src/ShellUI.CLI/README.md` | +28 −1 — theme init in Quick Start +
`theme` subcommand section |
| `src/ShellUI.Components/README.md` | +42 −5 — tweakcn command
references across Paths A/B/C + rewritten Theme Customization block |

Net: **+143 −14** across 4 files. No behavior change to `init`, `apply`,
or `update`; no new tests needed (composition of two already-tested
functions, smoke-tested end-to-end).

## Test plan

- [x] `dotnet test` — 76/76 green (unchanged; no new test surface)
- [x] `dotnet shellui theme --help` — lists `init`, `apply`, `update`
- [x] `dotnet shellui theme init --help` — shows `<url>` arg + `--force
/ --style / --tailwind / --yes` options
- [x] End-to-end in scratchpad `dotnet new blazor` project — both steps
complete, `input.css` contains `@import "tailwindcss"` + BEGIN/END
sentinel + 128 oklch vars, `shellui.theme.lock` written
- [x] Re-run `theme init` twice — second run is idempotent within the
sentinel region
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat/tweakcn-theme-import — bake tweakcn themes into projects at build time

2 participants