refactor(design): retire /design — a surface that could only inspect itself
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 40s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 9s
CI & Build / integration (push) Successful in 29s
CI & Build / TypeScript typecheck (push) Successful in 33s
CI & Build / Python tests (push) Successful in 59s
CI & Build / Build & push image (push) Successful in 40s
The design surface is for the projects an install tracks. /design read the running app's own stylesheet — names out of a bundled theme.css, values out of getComputedStyle(document.documentElement) — so it could only ever describe the instance serving the page. Scribe is one project among the projects Scribe tracks; it gets no view hardcoded into every install. The mechanism that makes this a tool rather than a mirror already existed and already covers Scribe: scripts/check_design_tokens.py runs in CI against a sheet path it knows nothing about, using check_code_against_tokens — the same engine behind check_snippets_against_system. /design was redundant even here. Removed: DesignView, DesignTabs (nothing left to tab between), api/design.ts, routes/design.py and its blueprint, the /design route, ui_design_system() and its setting, and the Settings picker that designated "this app's UI". utils/designTokens.ts and utils/designDrift.ts go with it — between them they were the browser-reading half. What survives is utils/designValues.ts, which works on a record rather than a document: valueForMode, modesPresent, and resolveDeclared. resolveDeclared gained real isolation in the move. Custom properties inherit and `all: initial` does not reset them, so a probe sitting in this page would resolve any reference a record leaves undeclared against the SURROUNDING app's tokens — previewing another project's system would quietly borrow this one's palette wherever that system was incomplete, and a token already reported under unknown_refs would render as though it were fine. Undeclared references are now blanked on the probe first, so they resolve to nothing, which is what the record says they are. Migration 0075 absorbs ui_design_system_id alongside design_rulebook_id rather than an 0076 undoing it: 0075 has not run anywhere, since dev is unmerged and deploys come from main. Both keys named a design source for the running install, and a project already carries its own pointer. This retires the agreement panel shipped yesterday. It asked whether the sheet was actually loaded and applied — the one question a record cannot answer about itself — but only ever about the app you are already inside. Nothing replaces it; recorded in #2430 rather than quietly dropped. Step 1 of milestone #274. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UaYUaouG9jjhATyuxCKrQs
This commit is contained in:
@@ -26,7 +26,6 @@ from scribe.routes.profile import profile_bp
|
||||
from scribe.routes.knowledge import knowledge_bp
|
||||
from scribe.routes.rulebooks import rulebooks_bp
|
||||
from scribe.routes.plugin import plugin_bp
|
||||
from scribe.routes.design import design_bp
|
||||
from scribe.routes.design_systems import design_systems_bp
|
||||
from scribe.routes.trash import trash_bp
|
||||
from scribe.routes.dashboard import dashboard_bp
|
||||
@@ -91,7 +90,6 @@ def create_app() -> Quart:
|
||||
app.register_blueprint(knowledge_bp)
|
||||
app.register_blueprint(rulebooks_bp)
|
||||
app.register_blueprint(plugin_bp)
|
||||
app.register_blueprint(design_bp)
|
||||
app.register_blueprint(design_systems_bp)
|
||||
app.register_blueprint(trash_bp)
|
||||
app.register_blueprint(dashboard_bp)
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
"""This install's UI surface — which design system it claims to be built from.
|
||||
|
||||
Kept separate from the design-systems CRUD blueprint on purpose. That one is
|
||||
the RECORD: create a system, move a token, read the cascade. This one answers a
|
||||
question about the RUNNING APP, and it exists because those are not the same
|
||||
question. A design system can be a perfect record of a stylesheet the app never
|
||||
loaded.
|
||||
|
||||
The client owns the other half. `utils/designTokens.ts` reads what the browser
|
||||
actually resolved, which is the one thing no server can report, and compares it
|
||||
to what this endpoint's system declares. So the comparison is
|
||||
"does the app agree with its own sheet?" rather than "is the record
|
||||
self-consistent?", which would be a tautology — the sheet is generated from the
|
||||
record (#2419).
|
||||
"""
|
||||
from quart import Blueprint, jsonify
|
||||
|
||||
from scribe.auth import get_current_user_id, login_required
|
||||
from scribe.services import design_systems as ds_svc
|
||||
|
||||
design_bp = Blueprint("design", __name__, url_prefix="/api/design")
|
||||
|
||||
|
||||
@design_bp.get("/ui-system")
|
||||
@login_required
|
||||
async def get_ui_system():
|
||||
"""The design system this install designated as the source of its own UI.
|
||||
|
||||
Returns `{"design_system_id": int|null, "title": str|null}`.
|
||||
|
||||
Both nulls is the NORMAL case, not an error — an install that has not
|
||||
designated one has nothing to check the running app against, and the client
|
||||
shows an explanatory empty state (rule #115).
|
||||
|
||||
An id with a null title is the third case and the reason the id is returned
|
||||
separately: designated, but deleted or not readable by this caller. Folding
|
||||
that into "none designated" is precisely how a feature comes to render a
|
||||
reassuring empty state forever.
|
||||
"""
|
||||
uid = get_current_user_id()
|
||||
system_id, system = await ds_svc.ui_design_system(uid)
|
||||
return jsonify({
|
||||
"design_system_id": system_id,
|
||||
"title": system.title if system else None,
|
||||
})
|
||||
@@ -37,7 +37,6 @@ from scribe.services.design_cascade import (
|
||||
resolve_tokens,
|
||||
would_cycle,
|
||||
)
|
||||
from scribe.services.settings import get_setting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -135,40 +134,6 @@ async def get_design_system(user_id: int, design_system_id: int) -> DesignSystem
|
||||
return system
|
||||
|
||||
|
||||
# Which design system this install's own UI is built from. A plain setting
|
||||
# rather than a column: no migration, discoverable in the Settings UI (rule
|
||||
# #25), and honest about being a per-install claim rather than a property of the
|
||||
# system — the same system can be the record for an app that never loads it.
|
||||
UI_DESIGN_SYSTEM_SETTING = "ui_design_system_id"
|
||||
|
||||
|
||||
async def ui_design_system(user_id: int) -> tuple[int | None, DesignSystem | None]:
|
||||
"""The design system this install says its UI is built from.
|
||||
|
||||
Returns `(id, system)`. Three outcomes, deliberately distinguishable:
|
||||
|
||||
- `(None, None)` — nothing designated. The NORMAL state for any install but
|
||||
the one that set it up (rule #115), not an error.
|
||||
- `(id, None)` — designated, but gone or not readable by this caller. A
|
||||
misconfiguration worth naming rather than silently degrading to "none",
|
||||
which is exactly the failure that orphaned the panel this feeds (#2419).
|
||||
- `(id, system)` — designated and readable.
|
||||
|
||||
A non-numeric setting value reads as nothing designated: the value is only
|
||||
ever written by a `<select>` of real ids, so garbage here means hand-edited
|
||||
or stale, and refusing to guess is better than raising on a page load.
|
||||
"""
|
||||
raw = (await get_setting(user_id, UI_DESIGN_SYSTEM_SETTING, "")).strip()
|
||||
if not raw:
|
||||
return None, None
|
||||
try:
|
||||
system_id = int(raw)
|
||||
except ValueError:
|
||||
logger.warning("Ignoring non-numeric %s: %r", UI_DESIGN_SYSTEM_SETTING, raw)
|
||||
return None, None
|
||||
return system_id, await get_design_system(user_id, system_id)
|
||||
|
||||
|
||||
async def list_design_systems(user_id: int) -> list[DesignSystem]:
|
||||
"""The caller's own systems, ordered by title. Empty is normal."""
|
||||
async with async_session() as session:
|
||||
|
||||
Reference in New Issue
Block a user