feat(phase 4d): public-listing fields so projax becomes the portfolio source of truth
Adds five additive columns on projax.items and propagates them through
every read/write path. flexsiebels.de (and any future portfolio renderer)
can now pull the public set via the MCP `list_items(public=true)` filter
and stop hard-coding project lists.
## Schema (migration 0014)
- public boolean default false (partial index when true)
- public_description text default ''
- public_live_url text default ''
- public_source_url text default ''
- public_screenshots text[] default '{}'
- items_unified view rebuilt to include the five new columns
- items_public_idx PARTIAL INDEX WHERE public = true (5% of rows)
## Store
- Item struct + scan/scanItems extended (5 cols)
- UpdateInput accepts the new fields with full-replace semantics
- new SetPublic(ids, bool) for bulk write
- SearchFilters gains Public *bool — nil = no filter
## MCP
- list_items: new `public` boolean filter (input schema + handler)
- update_item: 5 new partial-update fields (nil pointer = leave alone)
- itemView always emits the 5 fields (even when public=false) so consumers
can preview "what would publish" without a second round-trip
- 2 new integration tests against the DB
## Web
- /i/{path} grows a "Public listing" fieldset: toggle + textarea + 2 URL
inputs + screenshot list editor with add/remove rows + inline JS for
the editor. Values persist when public is off so toggling never
destroys typed-in content.
- /admin/bulk action bar gains "Make public" / "Make private" via a new
select; SQL update is a single statement per action.
- /?public=1 and /?public=0 chip parameters narrow the tree page.
Active() + QueryString() + TogglePublic() round-trip the state.
- parseScreenshotList helper trims + drops empties + preserves order
- 5 integration tests: migration landed, form round-trip, bulk action
round-trip, detail-page affordances, tree-filter narrowing
## docs/design.md §15
Documents the schema, MCP contract, UI surfaces, flexsiebels consumption
pattern, and what's NOT in scope (flexsiebels-side render, asset hosting,
approval workflows).
## Out of scope (per task brief)
- Flexsiebels rendering — separate task in m/flexsiebels.de after this ships
- Asset hosting (projax stores URLs, never bytes — same PER discipline)
- Multi-stage publish workflow (boolean is enough)
This commit is contained in:
@@ -664,3 +664,39 @@ header .theme-toggle .theme-icon {
|
||||
@media (max-width: 480px) {
|
||||
header .theme-toggle { padding: 6px 10px; font-size: 1em; min-height: 36px; }
|
||||
}
|
||||
|
||||
/* --- Public listing (Phase 4d) --- */
|
||||
fieldset.public-listing {
|
||||
margin: 20px 0 8px;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: var(--bg-alt);
|
||||
}
|
||||
fieldset.public-listing legend {
|
||||
font-weight: 600;
|
||||
font-size: 0.95em;
|
||||
padding: 0 6px;
|
||||
color: var(--accent);
|
||||
}
|
||||
fieldset.public-listing > p.muted {
|
||||
margin: 4px 0 12px;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
fieldset.public-listing label { margin-top: 8px; }
|
||||
.public-screenshots { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
|
||||
.public-screenshot-row { display: flex; gap: 6px; align-items: center; }
|
||||
.public-screenshot-row input[type=url] { flex: 1; }
|
||||
.public-screenshot-remove {
|
||||
background: var(--surface); color: var(--muted);
|
||||
border: 1px solid var(--border); border-radius: 3px;
|
||||
padding: 2px 8px; font-size: 1em; line-height: 1; cursor: pointer; min-height: 0;
|
||||
}
|
||||
.public-screenshot-remove:hover { color: var(--bad); border-color: var(--bad); }
|
||||
.public-screenshot-add {
|
||||
margin-top: 6px;
|
||||
background: transparent; color: var(--accent);
|
||||
border: 1px solid var(--border); border-radius: 4px;
|
||||
padding: 4px 10px; font-size: 0.9em; cursor: pointer; min-height: 0;
|
||||
}
|
||||
.public-screenshot-add:hover { background: var(--accent); color: var(--accent-fg); }
|
||||
|
||||
Reference in New Issue
Block a user