Web parity batch 2: DMs, Moderation, Sharing, Search, List Folders, Templates, X cross-post, profiles - #6
Merged
Merged
Conversation
Extends the schema DSL with select(a|b|c) single-choice columns (options on SchemaField.enumValues) and markdown long-text columns. Editor gains an inline options editor; row cells render a Picker (select) and a TextEditor+Textual preview (markdown). 25 new BDD tests. Reaches parity with the site's advertised type set (feature-gaps.md §1.1). SwiftUI-only; no wire/codec change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server already returned linkMetadata (title/description/image); the domain model dropped it. Adds Message.linkPreviews + a mapper that drops unparseable URLs, and a tappable LinkPreviewCardView (AsyncImage thumbnail + title + host) in the timeline. Render gate is forward-compatible pending fetchStatus docs. 16 new tests (feature-gaps.md §1.5). Not persisted in SwiftData by design. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Client-side template catalog (Blank / Meeting Notes / Daily Log / PRD) with a New-from-Template command (Shift-Opt-Cmd-N) and picker sheet that seeds the markdown body through the existing create path. Blank == prior behavior. 12 new tests (feature-gaps.md §1.4). No templates endpoint exists; static catalog can move server-side later. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pure renderer for documents, message threads, and lists-as-tables (schema-ordered columns, pipe/newline-escaped cells). Uses value-type Date.ISO8601Format for Sendable safety. 15 BDD tests. Client-side because /api/exports/* is CSV-only (see feature-blockages.md NB-3). UI wiring lands next (feature-gaps.md §1.3). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ed code Adds feature-gaps.md (parity review) and feature-blockages.md (backend asks, reconciled against blocker-prompts.md). Corrects 5 stale feature-status.md limitations (scheduled cancel/reschedule, watcher/org invite-by-handle, cross-post readiness, following scope) and de-stales two Help Book pages + regenerates the help index. Verified against current code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds P1-G (following feed), P1-H (GitHub issue writes), P2-F (Markdown export), P2-G (schema DSL select/markdown), P3-F (link-preview fetchStatus), P3-G (list clone), P3-H (PATCH/PUT) with paste-ready prompts. feature-blockages.md is now a concise index into the canonical blocker-prompts.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds an 'Export My Lists as Markdown' path: ExportViewModel paginates owned lists + rows and renders them via MarkdownExporter into a MarkdownFileDocument (.md), surfaced through a second fileExporter and a Markdown button on the Lists row. 4 new tests (happy/empty/failure/pagination). Completes feature-gaps §1.3 (engine + UI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the List-of-cells fallback in tableMode with a real SwiftUI Table using TableColumnForEach for one typed column per schema field (now valid at the macOS 15 deployment target — the 14.4 fallback comment was stale). Pagination becomes a Load-More footer since Table has no per-row appearance hook; cards mode keeps scroll-to-load. Completes feature-gaps §1.2 (grid; ERD still scoped separately). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…end + UI)
Close four top-priority web-parity gaps end-to-end against live-verified API
shapes, and consolidate the parity docs.
Features (Kit endpoints+DTOs → Domain services+mappers → SwiftUI MVVM + tests):
- G5 Search: GET /api/{messages,lists,documents}/search; global ⌘F search UI
reusing existing Message/List/Document mappers.
- G2 Moderation: block/mute/report + isBlocking; timeline/profile overflow-menu
actions + Settings "Blocked & Muted" pane. Removes the last AppKit dependency
from MessageRowView.
- G6 List Folders: /api/folders CRUD (subscriber-gated create, cycle-safe tree
builder); Lists sidebar folder tree.
- G1 Direct Messages: /api/dm folders/thread/send/recipients/unread + read/
trash/restore; inbox/thread/compose UI with threadUpdates polling, a
recipient-gated profile "Message" action, and an UnreadBadgeAggregator that
sums DM + notification unread counts. (SwiftData cache deferred — needs a
domain-side store seam.)
Docs:
- the-gaps.md: consolidated master parity doc (live-verified gap list, plan,
and progress table); supersedes feature-gaps.md.
- docs/api-coverage.md: re-baselined to the live ~151-endpoint surface; resolved
the public-profile (fn8) and native-OAuth-linking (fn12) footnotes.
Wire shapes verified live 2026-07-31 (read-only probes; DM object confirmed via
one authorized recon message that was then trashed).
Tests: InterlinedKit 224→250, InterlinedDomain 475→522, App 379→452
(+146 new, 0 regressions).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tokenized share links for lists and documents against live-verified shapes
(captured via a reversible create→capture→revoke on the test account).
- Kit: `Sharing` endpoint + DTOs — create/list/resolve/revoke/claim for both
lists (`/api/lists/{id}/share-links`, `/api/lists/shared/{token}`) and
documents (parallel paths).
- Domain: `SharingService` (subscriber-gated create via EntitlementsService),
`ShareRole` (watcher/collaborator/manager ↔ Viewer/Editor/Admin), `ShareLink`,
`ResolvedShare` (list/document resource), `ShareClaim`.
- Tests: +18 (7 Kit + 11 Domain). InterlinedKit 250→257, InterlinedDomain
522→533, 0 regressions.
Per-person collaborator grants (documents) remain a follow-up. App-layer Share
sheet + resolve/claim landing to follow.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing)
- Share Links panel (ShareLinksView/ViewModel over a ShareTarget list|document):
create with role + optional expiry, list active links, revoke (optimistic),
subscriber-gate → upsell. Surfaced from the Lists and Documents toolbars.
- Shared-resource landing (ResolveShareView/ViewModel): resolve a token, show
resource + role, claim when eligible; ownership-gated (hides claim / prompts
sign-in when no current user).
- ShareURLParser (pure) + ShareLinkDeepLink: recognizes …/lists|documents/
shared/{token} across https and interlinedlist://; called before the OAuth
callback path in onOpenURL and returns false for non-share URLs.
- Clipboard without AppKit via SwiftUI.ShareLink; AppEnvironment.sharing rebuilt
per-access with live entitlements (mirrors listFolders).
Tests: +38 (ShareLinksViewModel 12, ResolveShareViewModel 11, ShareURLParser 15).
App target 452→490, 0 failures. Packages unchanged (Kit 257, Domain 533,
Persistence 120). Decision-0003 grep gate clean.
Follow-ups: per-person collaborator grants; in-app sign-in from the landing;
post-claim deep-select of the specific resource.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…back)
`GET /api/users/{username}` now exists (verified live 2026-07-31) and is
strictly richer than the embedded-author fallback.
- Kit: `PublicProfileDTO` + `User.publicProfile(username:)`.
- Domain: `UserProfile.init(from: PublicProfileDTO)` (bio, follower/following
counts, joinedAt, isPrivate straight from the payload — no counts stitch).
- `SocialService.profile(username:)` now prefers the real endpoint; the
decision-0002 message-embedded-author fallback is retained only when the
endpoint 404s (pre-migration servers). Non-404 errors propagate.
- The existing profile UI renders the richer fields with no view changes.
Tests: +8 (2 Kit builder + rewired SocialService profile suite). InterlinedKit
257→259, InterlinedDomain 533→534, 0 regressions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…LinkedIn) Extends the existing Mastodon/Bluesky/LinkedIn cross-post feature to X/Twitter. Strictly additive — every pre-existing cross-post test stays green. - Kit: `.twitter` OAuthProvider (slug verified live), `Auth.twitterStatus()` + `TwitterStatusResponse`, and `crossPostToTwitter: Bool?` on CreateMessageRequest. - Domain: `MessagesService.createPost` gains a `crossPostToTwitter` param, folded into the `.crossPosting` entitlement gate and the request body. - App: X toggle in the composer cross-post section (mirrors LinkedIn), threaded through submit + result summary.⚠️ The `crossPostToTwitter` wire field name is PATTERN-MATCHED to the confirmed crossPostToBluesky/crossPostToLinkedIn naming but UNVERIFIED against live POST /api/messages (the test account has no linked X identity). If the server uses `crossPostToX`, only MessageDTO.CodingKeys needs to change — flagged in a doc comment on the field. Tests: +12. InterlinedKit 259→265, InterlinedDomain 534→536, App 490→492, Persistence 120 unchanged. 0 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The user's saved server-side templates (distinct from the client built-in
catalog), verified live 2026-07-31 (from-template shape captured via a
reversible create+delete).
- Kit: additive `Documents` extension — `templates()`, `createFromTemplate()`
(body `{templateDocumentId}`), `seedDefaultTemplates()` + DTOs.
- Domain: `DocumentTemplatesService` (own protocol, so no existing
DocumentsServicing conformance changes) + `DocumentTemplateRef`.
- Tests: +11 (5 Kit + 6 Domain). InterlinedKit 265→270, InterlinedDomain
536→542, 0 regressions.
Also documents live-API blockers found this session: Stripe billing routes and
org LinkedIn pages return 404 (not deployed); GitHub routes need a linked
identity. App-layer template picker + /api/documents/tree = follow-ups.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Surfaces the user's server-side templates in the existing "New from Template" flow as a second "Your templates" section alongside the built-in catalog. - `ServerTemplatesViewModel` (@mainactor) over `DocumentTemplatesServicing`: load + map templates, non-fatal load error, empty state with a "Seed defaults" affordance, create-from-template (debounced) → reload docs → select the new document. - `DocumentTemplatePickerView` now renders two sections under a unified TemplateSelection (.builtIn/.server); built-in behavior unchanged. - `DocumentTemplatesService` registered in AppEnvironment. Tests: +10 (ServerTemplatesViewModelTests). App target 492→502, packages unchanged (Kit 270, Domain 542, Persistence 120). 0 regressions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner decision 2026-07-31: billing stays in the online app; the native app never sells/manages subscriptions (it still reads customerStatus to gate subscriber features). G8 marked OUT OF SCOPE across the-gaps.md and api-coverage.md; Stripe rows dropped (also 404/not-deployed live). No code change — no in-app billing was ever built. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read the user's LinkedIn cross-post destinations so the composer can offer a target picker. Shapes verified live 2026-07-31. - Kit: `LinkedIn.postingTargets()` / `LinkedIn.targets()` + DTOs. - Domain: `LinkedInService.postingTargets()` → `LinkedInPostingTargets` (targets + orgScopeMissing); `LinkedInTarget` (personal/org/other). - Tests: +8. InterlinedKit 270→274, InterlinedDomain 542→546, 0 regressions. Org pages (G11b) remain deferred (routes 404/not-deployed). Composer target picker UI to follow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ocked)
Live probe 2026-07-31: GET /api/messages ignores feed/scope/following/filter
params (all return the 216-total "all" feed); POST /api/user/update
{viewingPreference} → 405. No client-side following-feed path exists.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Web parity — batch 2
Closes the highest-priority feature gaps between the macOS native app and the live web app (interlinedlist.com), each built backend-first against live-verified API shapes and wired end-to-end (Kit → Domain → App) with BDD tests. Continues after the merged PR #5.
Features shipped (9)
UnreadBadgeAggregator)MessageRowView)interlinedlist://…/shared/{token}and pasted URLsGET /api/users/{username}(bio, follower/following counts, join date, private flag), replacing the decision-0002 embedded-author fallbackcrossPostToTwitter)Tests
All green, 0 regressions:
swift test; App target viaxcodebuild test CODE_SIGNING_ALLOWED=NO(ad-hoc-vs-team-signed test-host quirk on the build machine — worth a project-level fix).Docs
the-gaps.md— consolidated master parity doc (live-verified gap list, plan, progress table); supersedesfeature-gaps.md.docs/api-coverage.md— re-baselined to the live ~151-endpoint surface (was stale at 98); public-profile & OAuth-linking footnotes resolved.Out of scope
customerStatusfor feature-gating. (Stripe routes were also 404/not-deployed.)Verified-but-blocked (need backend/infra, not native work)
GET /api/messagesignores feed/scope params;viewingPreferenceupdate 405s./api/auth/accountsis session-cookie-only (401 under Bearer)..pkgmodel.Method note
Wire shapes were verified against the live API using the dedicated
.envtest account — read-only probes throughout, plus a few reversible recon writes (one DM, one share link, one from-template doc) each created and then cleaned up.Not included
The in-progress Document Sync Agent (
SyncAgent/) and theblog-post/assets are a separate feature and were intentionally left out of this PR (staged locally, unrelated to web parity).🤖 Generated with Claude Code