Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 83 additions & 58 deletions apps/daemon/src/design-systems/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,68 +337,93 @@ export async function listDesignSystems(
}
for (const entry of entries) {
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
const brandRoot = path.join(root, entry.name);
const manifest = await readProjectManifest(brandRoot, entry.name);
const designPath = path.join(brandRoot, manifest?.files.design ?? 'DESIGN.md');
try {
const stats = await stat(designPath);
if (!stats.isFile()) continue;
const raw = await readFile(designPath, 'utf8');
const metadata = await readUserMetadata(root, entry.name);
if (!designSystemVisibleFromWorkspace(metadata.workspaceId, options.workspaceId)) continue;
const { data: frontmatter, body } = parseFrontmatter(raw);
const titleMatch = /^#\s+(.+?)\s*$/m.exec(body);
const markdownTitle =
titleMatch?.[1] !== undefined ? cleanTitle(titleMatch[1]) : '';
const fallbackTitle = markdownTitle || stringField(frontmatter, 'name') || entry.name;
const title = cleanTitle(
metadata.title
?? manifest?.name
?? fallbackTitle,
);
const frontmatterCategory = stringField(frontmatter, 'category');
const category = (
metadata.category
?? manifest?.category
?? extractCategory(body)
?? frontmatterCategory
) || 'Uncategorized';
const markdownSummary = summarize(body);
const markdownSwatches = extractSwatches(body);
const frontmatterSwatchRow = swatchesFromFrontmatter(frontmatter);
const swatches = pickFinalSwatchRow(frontmatterSwatchRow, markdownSwatches);
out.push({
id: `${options.idPrefix ?? ''}${entry.name}`,
title,
category,
summary:
(manifest?.description?.trim() || markdownSummary)
|| stringField(frontmatter, 'description')
|| '',
swatches,
surface:
metadata.surface
?? extractSurface(body)
?? frontmatterSurface(frontmatter)
?? 'web',
body: raw,
source: options.source ?? 'built-in',
status: metadata.status ?? options.defaultStatus ?? 'published',
isEditable: options.isEditable ?? false,
...(metadata.createdAt ? { createdAt: metadata.createdAt } : {}),
...(metadata.updatedAt ? { updatedAt: metadata.updatedAt } : {}),
...(metadata.provenance ? { provenance: metadata.provenance } : {}),
...(metadata.projectId ? { projectId: metadata.projectId } : {}),
...(metadata.teamSynced ? { teamSynced: true } : {}),
...(metadata.workspaceId ? { workspaceId: metadata.workspaceId } : {}),
});
} catch {
// Skip.
}
const summary = await readDesignSystemSummaryFromDirectory(root, entry.name, options);
if (summary) out.push(summary);
}
return out;
}

export async function readDesignSystemSummary(
root: string,
id: string,
options: DesignSystemListOptions = {},
): Promise<DesignSystemSummary | null> {
const dirId = stripPrefixAndValidateId(id, options.idPrefix);
if (!dirId) return null;
try {
const entries = await readdir(root);
if (!entries.includes(dirId)) return null;
} catch {
return null;
}
return readDesignSystemSummaryFromDirectory(root, dirId, options);
}

async function readDesignSystemSummaryFromDirectory(
root: string,
dirId: string,
options: DesignSystemListOptions,
): Promise<DesignSystemSummary | null> {
const brandRoot = path.join(root, dirId);
const manifest = await readProjectManifest(brandRoot, dirId);
const designPath = path.join(brandRoot, manifest?.files.design ?? 'DESIGN.md');
try {
const stats = await stat(designPath);
if (!stats.isFile()) return null;
const raw = await readFile(designPath, 'utf8');
const metadata = await readUserMetadata(root, dirId);
if (!designSystemVisibleFromWorkspace(metadata.workspaceId, options.workspaceId)) return null;
const { data: frontmatter, body } = parseFrontmatter(raw);
const titleMatch = /^#\s+(.+?)\s*$/m.exec(body);
const markdownTitle =
titleMatch?.[1] !== undefined ? cleanTitle(titleMatch[1]) : '';
const fallbackTitle = markdownTitle || stringField(frontmatter, 'name') || dirId;
const title = cleanTitle(
metadata.title
?? manifest?.name
?? fallbackTitle,
);
const frontmatterCategory = stringField(frontmatter, 'category');
const category = (
metadata.category
?? manifest?.category
?? extractCategory(body)
?? frontmatterCategory
) || 'Uncategorized';
const markdownSummary = summarize(body);
const markdownSwatches = extractSwatches(body);
const frontmatterSwatchRow = swatchesFromFrontmatter(frontmatter);
const swatches = pickFinalSwatchRow(frontmatterSwatchRow, markdownSwatches);
return {
id: `${options.idPrefix ?? ''}${dirId}`,
title,
category,
summary:
(manifest?.description?.trim() || markdownSummary)
|| stringField(frontmatter, 'description')
|| '',
swatches,
surface:
metadata.surface
?? extractSurface(body)
?? frontmatterSurface(frontmatter)
?? 'web',
body: raw,
source: options.source ?? 'built-in',
status: metadata.status ?? options.defaultStatus ?? 'published',
isEditable: options.isEditable ?? false,
...(metadata.createdAt ? { createdAt: metadata.createdAt } : {}),
...(metadata.updatedAt ? { updatedAt: metadata.updatedAt } : {}),
...(metadata.provenance ? { provenance: metadata.provenance } : {}),
...(metadata.projectId ? { projectId: metadata.projectId } : {}),
...(metadata.teamSynced ? { teamSynced: true } : {}),
...(metadata.workspaceId ? { workspaceId: metadata.workspaceId } : {}),
};
} catch {
return null;
}
}

/**
* Whether a design system claimed by `owner` should be listed while `scope` is
* the active workspace.
Expand Down
40 changes: 34 additions & 6 deletions apps/daemon/src/design-systems/server-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,17 @@ type DesignSystemWorkspaceOptions = {
exactTeam?: boolean;
};

function canonicalizeBuiltInDesignSystem(
summary: DesignSystemSummary,
): DesignSystemSummary {
return {
...summary,
source: 'built-in',
isEditable: false,
status: 'published',
};
}

export type DesignSystemAssetSyncOutcome =
| { ok: true; synced: string[] }
| { ok: false; reason: 'not-found' | 'no-workspace-project' };
Expand Down Expand Up @@ -180,6 +191,7 @@ export function createDesignSystemServerServices({
};
designSystems: {
listDesignSystems: (root: string, options?: DesignSystemListOptions) => Promise<DesignSystemSummary[]>;
readDesignSystemSummary: (root: string, id: string, options?: DesignSystemListOptions) => Promise<DesignSystemSummary | null>;
readDesignSystem: (root: string, id: string, options?: Pick<DesignSystemListOptions, 'idPrefix' | 'workspaceId'>) => Promise<string | null | undefined>;
readDesignSystemPackageInfo: (root: string, id: string, options?: Pick<DesignSystemListOptions, 'idPrefix' | 'workspaceId'>) => Promise<unknown>;
readDesignSystemStaticFile: (root: string, id: string, filePath: string, options?: Pick<DesignSystemListOptions, 'idPrefix' | 'workspaceId'>) => Promise<DesignSystemStaticFile | null | undefined>;
Expand Down Expand Up @@ -363,12 +375,8 @@ export function createDesignSystemServerServices({
exactTeam?: boolean;
exactPersonal?: boolean;
} = {}) {
const builtIn = (await designSystems.listDesignSystems(paths.DESIGN_SYSTEMS_DIR)).map((s) => ({
...s,
source: 'built-in',
isEditable: false,
status: 'published',
}));
const builtIn = (await designSystems.listDesignSystems(paths.DESIGN_SYSTEMS_DIR))
.map(canonicalizeBuiltInDesignSystem);
let installed: DesignSystemSummary[] = [];
try {
installed = await designSystems.listDesignSystems(paths.USER_DESIGN_SYSTEMS_DIR, {
Expand Down Expand Up @@ -458,6 +466,25 @@ export function createDesignSystemServerServices({
});
}

async function readAvailableDesignSystemSummary(
id: string,
options: {
workspaceId?: string | null;
workspaceMemberId?: string | null;
exactTeam?: boolean;
exactPersonal?: boolean;
} = {},
): Promise<DesignSystemSummary | null> {
if (!id.startsWith('user:')) {
const summary = await designSystems.readDesignSystemSummary(paths.DESIGN_SYSTEMS_DIR, id);
return summary ? canonicalizeBuiltInDesignSystem(summary) : null;
}
// User and Team systems still go through the catalog's persisted binding
// filters. The direct path is intentionally limited to public bundled
// presets, whose id maps to one immutable repository directory.
return (await listAllDesignSystems(options)).find((system) => system.id === id) ?? null;
}

async function readAvailableDesignSystem(
id: string,
options: {
Expand Down Expand Up @@ -957,6 +984,7 @@ export function createDesignSystemServerServices({
listAllSkillLikeEntries,
listAllSkills,
readAvailableDesignSystem,
readAvailableDesignSystemSummary,
readAvailableDesignSystemPackageInfo,
readAvailableDesignSystemStaticFile,
readDesignSystemWorkspaceTextFile,
Expand Down
39 changes: 26 additions & 13 deletions apps/daemon/src/routes/design-systems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export interface RegisterDesignSystemRoutesDeps extends RouteDeps<'db' | 'paths'
id: string,
options?: { workspaceId?: string | null; workspaceMemberId?: string | null; exactTeam?: boolean },
) => Promise<string | null>;
readAvailableDesignSystemSummary: (
id: string,
options?: { workspaceId?: string | null; workspaceMemberId?: string | null; exactTeam?: boolean },
) => Promise<AvailableDesignSystemSummary | null>;
readAvailableDesignSystemPackageInfo: (
id: string,
options?: { workspaceId?: string | null; workspaceMemberId?: string | null; exactTeam?: boolean },
Expand Down Expand Up @@ -216,6 +220,7 @@ export function registerDesignSystemRoutes(
listUserDesignSystemRevisions,
prepareDesignTokenContractRebuild,
readAvailableDesignSystem,
readAvailableDesignSystemSummary,
readAvailableDesignSystemPackageInfo,
readAvailableDesignSystemStaticFile,
readDesignSystemWorkspaceTextFile,
Expand All @@ -233,6 +238,17 @@ export function registerDesignSystemRoutes(
{ workspaceId: string; workspaceMemberId: string } | null
>();

const resolveAvailableDesignSystemSummary = async (
id: string,
options: {
workspaceId?: string | null;
workspaceMemberId?: string | null;
exactTeam?: boolean;
} = {},
): Promise<AvailableDesignSystemSummary | undefined> => {
return (await readAvailableDesignSystemSummary(id, options)) ?? undefined;
};

const getBoundDesignSystem = (
dbHandle: unknown,
workspaceId: string,
Expand Down Expand Up @@ -333,11 +349,13 @@ export function registerDesignSystemRoutes(
binding = personalBinding;
}
}
const isPublicBuiltIn = resolution.context && !binding
? (await listAllDesignSystems({
const publicSummary = resolution.context && !binding
? await resolveAvailableDesignSystemSummary(id, {
workspaceId: resolution.context.workspaceId,
})).some((system) => system.id === id && system.source === 'built-in')
: false;
workspaceMemberId: resolution.context.workspaceMemberId,
})
: undefined;
const isPublicBuiltIn = publicSummary?.source === 'built-in';
// Explicit Workspace requests never inherit ownerless legacy resources.
// A Personal design system is private to its exact persisted creator even
// when the caller is an owner/admin in the same Workspace. Team resources
Expand Down Expand Up @@ -693,21 +711,16 @@ export function registerDesignSystemRoutes(
const workspaceId = headerValue(req, 'x-od-workspace-id');
const workspaceMemberId = headerValue(req, 'x-od-workspace-member-id');
const storage = resolveDesignSystemStorage(req, req.params.id);
const systems = await listAllDesignSystems({
const summary = await resolveAvailableDesignSystemSummary(req.params.id, {
workspaceId,
workspaceMemberId,
exactTeam: storage.exactTeam,
});
const summary = systems.find((s) => s.id === req.params.id);
const projectBody = await readDesignSystemWorkspaceTextFile(db, summary, 'DESIGN.md');
const body = projectBody ?? await readAvailableDesignSystem(req.params.id, {
workspaceId,
workspaceMemberId,
exactTeam: storage.exactTeam,
});
if (body === null || !summary) {
if (!summary) {
return res.status(404).json({ error: 'design system not found' });
}
const projectBody = await readDesignSystemWorkspaceTextFile(db, summary, 'DESIGN.md');
const body = projectBody ?? summary.body;
const packageInfo = await readAvailableDesignSystemPackageInfo(req.params.id, {
workspaceId,
workspaceMemberId,
Expand Down
4 changes: 4 additions & 0 deletions apps/daemon/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ import {
listUserDesignSystemFiles,
listUserDesignSystemRevisions,
readDesignSystem,
readDesignSystemSummary,
readDesignSystemPackageInfo,
readDesignSystemStaticFile,
readUserDesignSystemFile,
Expand Down Expand Up @@ -2708,6 +2709,7 @@ export async function startServer({
designSystems: {
listDesignSystems,
readDesignSystem,
readDesignSystemSummary,
readDesignSystemPackageInfo,
readDesignSystemStaticFile,
listUserDesignSystemFiles,
Expand Down Expand Up @@ -2763,6 +2765,7 @@ export async function startServer({
listAllSkillLikeEntries,
listAllSkills,
readAvailableDesignSystem,
readAvailableDesignSystemSummary,
readAvailableDesignSystemPackageInfo,
readAvailableDesignSystemStaticFile,
readDesignSystemWorkspaceTextFile,
Expand Down Expand Up @@ -8118,6 +8121,7 @@ export async function startServer({
listUserDesignSystemRevisions,
prepareDesignTokenContractRebuild,
readAvailableDesignSystem,
readAvailableDesignSystemSummary,
readAvailableDesignSystemPackageInfo,
readAvailableDesignSystemStaticFile,
readDesignSystemWorkspaceTextFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ async function startAuthorityServer(options: {
decision: { available: false },
}) as never,
readAvailableDesignSystem: async () => summary.body,
readAvailableDesignSystemSummary: async () => summary,
readAvailableDesignSystemPackageInfo: async () => null,
readAvailableDesignSystemStaticFile: calls.static,
readDesignSystemWorkspaceTextFile: async () => null,
Expand Down
Loading
Loading