Sync subrepo branch brightdigit-com-260717 - #1
Conversation
…3 HTMLFactory seam (#157)
Medium: - TailwindKit: fix negative spacing emitting invalid `--N` classes for every utility except margins. Route top/right/bottom/left/inset, gap(X/Y), and space(X/Y) through a shared DefaultSpacing.className(prefix:) that moves the `-` ahead of the prefix. Add negative-spacing coverage. - import buttondown: make repeated imports idempotent. Filter emails to the genuinely-new ones before numbering, keyed on explicit subject number OR slug (existingSlugs), so unnumbered sent emails are no longer re-imported under a fresh number each run. Add existingSlugs(in:) reader + regression test. - CI: add ContributeButtondown (orphaned test target) and the other split packages to .github/packages.json so their tests/builds run again. Low: - Restore raw <img> (no alt) across the Post/Podcast/Newsletter cards for exact markup parity; Plot's Image(_:) was injecting alt="". - Remove the invalid bare `gap` TailwindKit utility. - existingIssueNumbers: distinguish a missing directory (silent) from a real read failure (logged) instead of swallowing both via try?. - Align split-package macOS floor to .v15 (matches root/Publish). - Bump Styling/.nvmrc to Node 20 and declare the engines floor for Tailwind v4. - Correct the reconcile existingStatus doc to match the captured-at-planning write guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# Conflicts: # .gitignore # Package.swift # README.md
|
Important Review skippedToo many files! This PR contains 112 files, which is 12 over the limit of 100. To get a review, narrow the scope: Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (126)
You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Keep monorepo path deps in Package.swift; rewrite to revision-pinned URLs in standalone CI via Scripts/ensure-remote-deps.sh. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
Plot's working branch is gone; Package.swift already uses url+branch so the dual-mode rewrite script is obsolete. Co-authored-by: Cursor <cursoragent@cursor.com>
Wave 0 release PRs are merged to main, so the v1.0.0 branches are stale leftovers rather than the consumer pin. Point Wave 0 dependencies at `branch: "main"`. Wave 1/2 dependencies stay on their brightdigit-com-* working branches, which are not yet merged to main. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Wave 0 repin left the committed lockfile stale, which hard-fails CI
that runs with automatic resolution disabled ("an out-of-date resolved
file was detected"). Regenerate it so the lockfile matches the manifest.
These packages have no local `path:` dependencies left, so the
skip-package-resolved workflow flag is no longer needed — the committed
Package.resolved is now the real lockfile and should be honored by CI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TailwindKit shipped without a license file. Add the standard BrightDigit MIT license so the README License badge and SPI license detection resolve. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DangerPR.yml and TailwindKitTest.yml both run on macos-12, a retired runner label, so their jobs queue forever and never report — they are the two non-successful checks on PR #1. The package CI in TailwindKit.yml already covers build and test on every platform, so delete both workflows along with the Dangerfile they drove. Also drop the abandoned CHANGELOG.md and CONTRIBUTING/ directory (last touched in 2022, superseded by RELEASE_NOTES.md and the shared BrightDigit contribution process), and re-sync .swiftlint.yml with the donor now that the Dangerfile.swift exclusion is moot. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- fail-fast: true on all four matrix legs (ubuntu, windows, macOS platforms, android) so a broken leg stops the run instead of burning the whole matrix. - Ubuntu coverage step moves off the SHA-pinned brightdigit fork of swift-coverage-action back to sersoft-gmbh/swift-coverage-action@v5, and drops fail-on-empty-output. - Drop verbose: true from the Codecov upload. - build-macos-platforms: remove the ENABLE_WATCHOS step gate and add the visionOS row. - Re-sync claude.yml with the template. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Vendor .claude/agent-notes.md and the 10 shared .claude/skills/ directories as real directories. - Rename CLAUDE.md to AGENTS.md as the canonical agent instruction file, leave CLAUDE.md as a symlink to it, and append the Memory & Corrections Convention section pointing at .claude/agent-notes.md. - Add .spi.yml so Swift Package Index builds documentation for the TailwindKit target on Swift 6.4. - codecov.yml ignore pattern -> Tests/**/* so nested test sources are matched. - Scripts/lint.sh: drop the ButtondownKit-specific Generated/ file enumeration (TailwindKit has no generated sources) in favour of --recursive Sources Tests, and force periphery onto the native build system so it finds the index store under Swift 6.4. The header.sh -p argument stays TailwindKit. - Add RELEASE_NOTES.md describing what this branch changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New TailwindKit.docc catalog with a real landing page: overview, the capability-protocol and token model, variants, arbitrary values, the Plot bridge, and a Topics tree covering every public symbol. - Placeholder TailwindKitLogo.png in the catalog Resources, referenced from the README header. - README rewritten on the standard BrightDigit structure: logo, the eight-badge block, tagline, What is TailwindKit?, Installation, Usage, Testing, Requirements and License. Requirements now state Swift 6.4 / macOS 13 / Ubuntu Noble to match Package.swift and CI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove comments describing behavior the workflow no longer has: the coverage step is not a SHA-pinned brightdigit fork (it is sersoft-gmbh@v5), and the watchOS leg is not gated on an ENABLE_WATCHOS repo variable. Comment-only; no step, action, or matrix row changes. Mirrors brightdigit/Contribute#18 in the donor. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| @@ -1,11 +1,158 @@ | |||
|  | |||
There was a problem hiding this comment.
Done in b25bd3d, using the SVG you sent.
Worth recording why it needed fixing: the committed PNG wasn't a stale or low-res version of the right mark — it was different artwork. A 1200×630 wide gradient banner with a "TailwindKit" wordmark and only the double-wave glyph. Your SVG is a 106×106 square icon in Tailwind's own sky-blue (#38bdf8) with the Swift bird and the wave glyph.
Now ships, rendered from your file with rsvg-convert:
TailwindKitLogo.svg— vector source of truthTailwindKitLogo.png— 600×600TailwindKitLogo@2x.png— 1200×1200
matching the .svg + .png + @2x pattern ContributeWordPress uses. The README image path is unchanged, so it picks up the new artwork automatically.
| ], | ||
| dependencies: [ | ||
| .package( | ||
| url: "https://github.com/brightdigit/Plot.git", |
There was a problem hiding this comment.
should we consider removing Plot dependency
There was a problem hiding this comment.
Yes — removed, in b7a2dba. TailwindKit now has zero dependencies, not even Foundation.
I measured it before deciding: Plot was reachable from 1 of 56 source files, all 4 test files were already Plot-free, and the bridge was 3 functions that each reduced to .class(style.rendered). Your call — protocol in TailwindKit, extension in BrightDigit — is what shipped:
public protocol TailwindClassAttribute {
static func tailwindClass(_ className: String) -> Self
}
extension TailwindClassAttribute {
public static func tailwind(_ style: TailwindStyleBuilder) -> Self {
.tailwindClass(style.rendered)
}
}BrightDigit supplies the binding in Sources/BrightDigitSite/Nodes/Node+Tailwind.swift — two conditional conformances. Services+Box.swift is byte-identical; bigImage.tailwind(.rounded(.lg)) still compiles and renders the same, verified against the root build and ServicesBoxTests.
I verified leading-dot inference by compiling a probe before committing to this, with a negative control to prove the probe was real. Node.div(.tailwind(.flex.gap(4))) still infers unannotated, nested arbitrarily deep, interleaved with Plot's own .class(…)/.id(…). One conditional conformance covers every Context — no per-context work.
Two things you'll want to know:
1. Removing Plot alone would not have compiled. TailwindKit had no import Foundation anywhere, but four files call String.replacingOccurrences — they were only building because import Plot leaked Foundation in transitively. I added escapingSpaces(_:) (a map over characters) in Core/ArbitraryValue.swift, which is why the package ends at genuinely zero dependencies rather than trading Plot for Foundation. Now covered by tests with real spaces (w-[calc(100%_-_2rem)]).
2. Plot's Component can't use the protocol. Swift rejects retroactively conforming a protocol to another protocol (extension of protocol 'Component' cannot declare inheritance relationship), and Component.class(_:replaceExisting:) returns an existential rather than Self. So Component.tailwind stays a hand-written one-liner on the BrightDigit side. That asymmetry is a language limitation, and it's documented in the protocol so nobody "fixes" it later.
CI is green across Ubuntu, Android, Windows (2022 + 2025), macOS, iOS/tvOS/watchOS/visionOS — the Linux and Windows runs being the ones that actually prove the Foundation removal. 48 tests pass, lint clean (0 violations / 62 files), periphery reports no unused code.
Since it now depends on nothing, TailwindKit could tag alongside Wave 0 rather than Wave 1 — I left it in Wave 1 because nothing needs it earlier, but it's no longer constrained.
The committed PNG was the wrong artwork: a 1200x630 gradient banner with a wordmark. The actual mark is a square icon — a Swift bird over the Tailwind double-wave glyph on a #38bdf8 rounded square. Ships the vector source alongside 600x600 and @2x renders, matching the .svg + .png + @2x pattern used elsewhere in the family. The README image path is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TailwindKit models Tailwind utilities; it never needed an HTML library. Plot was reachable from exactly one of 56 source files, and only to spell .class(style.rendered). - Add TailwindClassAttribute: one static requirement, tailwindClass(_:) -> Self, with .tailwind(_:) supplied by a protocol extension. Consumers conditionally conform their own element type; leading-dot inference resolves through the extension, so `Node.div(.tailwind(.flex.gap(4)))` still reads natively. - Delete Sources/TailwindKit/Plot/ and the Plot dependency. - Add escapingSpaces(_:) so arbitrary-value escaping no longer needs String.replacingOccurrences. Four files were relying on Foundation leaking in transitively through Plot, so removing Plot alone would not have compiled. The package now has no dependencies at all — not even Foundation. Plot's Component keeps a hand-written one-liner on the consumer side: a protocol cannot be retroactively conformed to another protocol, and Component.class returns an existential rather than Self. That asymmetry is a Swift limitation and is documented in the protocol. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Matches the Contribute-family packages, which all point at the vector asset and keep the rasters as companions. The SVG carried only a viewBox, so a consumer rendering it (GitHub included) would scale it to the full container width. Pinned to a 200x200 presentation size, the same ~200px height the sibling logos use. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two review follow-ups, both API shape only — no rendered output changes.
Rename the `TailwindClassAttribute` requirement from `tailwindClass(_:)` to
`class(_:)`, the factory HTML libraries already declare. Plot's `Node` and
`Attribute` both expose
public static func `class`(_ className: String) -> Self
under `Context: HTMLContext`, matching on argument label, return type and
constraint — so a consumer's conformance is now a declaration with an empty
body rather than a forwarding one-liner:
extension Node: TailwindClassAttribute where Context: HTMLContext {}
`Component` still needs its instance-method sugar written directly: it is a
protocol (Swift forbids retroactively conforming a protocol to another) and its
`class` takes an extra parameter and returns an existential, not `Self`.
Move the file-scope `escapingSpaces(_:)` to `String.escapingSpaces` in
`Core/String+ArbitraryValue.swift` — no global functions. The file is renamed
because SwiftLint's `file_name` rule runs at `severity: error`. This also
removes the private shim in `ArbitraryStyling` that existed only to disambiguate
against the global; a `String` member cannot be shadowed that way. The body
stays a `map` over characters, keeping the package free of Foundation.
The stub in `TailwindClassAttributeTests` now declares only `class(_:)` and no
protocol-specific member, so the suite failing to compile is itself the
assertion that conformance stays implementation-free.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Draft PR establishing the initial package content for this split-off subrepo.
The subrepo branch was pushed from the brightdigit.com monorepo via
git subrepo push;mainwas tied in with a--allow-unrelated-historiesmerge so this diff is reviewable.🤖 Generated with Claude Code