@upstart.gg/vite-plugins 0.1.83 → 0.1.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/site-theme.d.ts +55 -0
- package/dist/site-theme.d.ts.map +1 -0
- package/dist/site-theme.js +184 -0
- package/dist/site-theme.js.map +1 -0
- package/dist/upstart-editor-api.d.ts +6 -1
- package/dist/upstart-editor-api.d.ts.map +1 -1
- package/dist/upstart-editor-api.js +10 -1
- package/dist/upstart-editor-api.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/index.js +12 -0
- package/dist/vite-plugin-upstart-editor/runtime/index.js.map +1 -1
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts +3 -0
- package/dist/vite-plugin-upstart-editor/runtime/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/site-theme.ts +205 -0
- package/src/tests/site-theme.test.ts +122 -0
- package/src/upstart-editor-api.ts +31 -0
- package/src/vite-plugin-upstart-editor/runtime/index.ts +15 -0
- package/src/vite-plugin-upstart-editor/runtime/types.ts +2 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import z from "zod";
|
|
2
|
+
|
|
3
|
+
//#region src/site-theme.d.ts
|
|
4
|
+
/** The theme colors the panel edits, in the theme file's naming. */
|
|
5
|
+
declare const EDITABLE_THEME_COLORS: readonly ["base100", "base200", "base300", "baseContent", "primary", "primaryContent", "secondary", "secondaryContent", "accent", "accentContent", "neutral", "neutralContent"];
|
|
6
|
+
type EditableThemeColor = (typeof EDITABLE_THEME_COLORS)[number];
|
|
7
|
+
type ThemeColors = Partial<Record<EditableThemeColor, string>>;
|
|
8
|
+
declare const payloadGetTheme: z.ZodObject<{
|
|
9
|
+
action: z.ZodLiteral<"getTheme">;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
type PayloadGetTheme = z.infer<typeof payloadGetTheme>;
|
|
12
|
+
declare const payloadSetTheme: z.ZodObject<{
|
|
13
|
+
action: z.ZodLiteral<"setTheme">;
|
|
14
|
+
colors: z.ZodRecord<z.ZodEnum<{
|
|
15
|
+
accent: "accent";
|
|
16
|
+
accentContent: "accentContent";
|
|
17
|
+
base100: "base100";
|
|
18
|
+
base200: "base200";
|
|
19
|
+
base300: "base300";
|
|
20
|
+
baseContent: "baseContent";
|
|
21
|
+
neutral: "neutral";
|
|
22
|
+
neutralContent: "neutralContent";
|
|
23
|
+
primary: "primary";
|
|
24
|
+
primaryContent: "primaryContent";
|
|
25
|
+
secondary: "secondary";
|
|
26
|
+
secondaryContent: "secondaryContent";
|
|
27
|
+
}> & z.core.$partial, z.ZodString>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
type PayloadSetTheme = z.infer<typeof payloadSetTheme>;
|
|
30
|
+
interface ThemePalette {
|
|
31
|
+
/** Stable id: "current" for the chosen mockup, "palette-<n>" for its variants. */
|
|
32
|
+
id: string;
|
|
33
|
+
/** The catalog palette's description, when known (e.g. "Blush base, deep rose text…"). */
|
|
34
|
+
label: string;
|
|
35
|
+
colors: ThemeColors;
|
|
36
|
+
}
|
|
37
|
+
type GetThemeResult = {
|
|
38
|
+
success: true;
|
|
39
|
+
themeId: string;
|
|
40
|
+
colors: ThemeColors;
|
|
41
|
+
palettes: ThemePalette[];
|
|
42
|
+
} | {
|
|
43
|
+
success: false;
|
|
44
|
+
error: string;
|
|
45
|
+
};
|
|
46
|
+
type SetThemeResult = {
|
|
47
|
+
success: true;
|
|
48
|
+
filePaths: string[];
|
|
49
|
+
} | {
|
|
50
|
+
success: false;
|
|
51
|
+
error: string;
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { EDITABLE_THEME_COLORS, EditableThemeColor, GetThemeResult, PayloadGetTheme, PayloadSetTheme, SetThemeResult, ThemeColors, ThemePalette, payloadGetTheme, payloadSetTheme };
|
|
55
|
+
//# sourceMappingURL=site-theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-theme.d.ts","names":[],"sources":["../src/site-theme.ts"],"mappings":";;;;cAiBa,qBAAA;AAAA,KAcD,kBAAA,WAA6B,qBAAA;AAAA,KAC7B,WAAA,GAAc,OAAA,CAAQ,MAAA,CAAO,kBAAA;AAAA,cAS5B,eAAA,EAAe,CAAA,CAAA,SAAA;;;KAChB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,eAAA;AAAA,cAEhC,eAAA,EAAe,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;KAIhB,eAAA,GAAkB,CAAA,CAAE,KAAA,QAAa,eAAA;AAAA,UAE5B,YAAA;;EAEf,EAAA;;EAEA,KAAA;EACA,MAAA,EAAQ,WAAA;AAAA;AAAA,KAGE,cAAA;EACN,OAAA;EAAe,OAAA;EAAiB,MAAA,EAAQ,WAAA;EAAa,QAAA,EAAU,YAAA;AAAA;EAC/D,OAAA;EAAgB,KAAA;AAAA;AAAA,KAEV,cAAA;EAAmB,OAAA;EAAe,SAAA;AAAA;EAA0B,OAAA;EAAgB,KAAA;AAAA"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import z from "zod";
|
|
4
|
+
//#region src/site-theme.ts
|
|
5
|
+
/**
|
|
6
|
+
* The site's colors, editable from the editor's "Colors" panel.
|
|
7
|
+
*
|
|
8
|
+
* A site's colors live in its theme file (`app/config/themes/<id>.json`, the one `site.json` names
|
|
9
|
+
* as the default), read by the theme plugin at build time. A new site also keeps the palettes the
|
|
10
|
+
* client was offered when choosing its look (`docs/mockups/<direction>/` and its `palettes/<n>/`
|
|
11
|
+
* variants, see the `PaletteVariants` tool): the panel offers them again, next to a free picker.
|
|
12
|
+
*
|
|
13
|
+
* Saving also records in `docs/DESIGN.md` that the user chose the colors themselves: agents read
|
|
14
|
+
* the brief before any UI work, and would otherwise bring the brief's palette back.
|
|
15
|
+
*/
|
|
16
|
+
/** The theme colors the panel edits, in the theme file's naming. */
|
|
17
|
+
const EDITABLE_THEME_COLORS = [
|
|
18
|
+
"base100",
|
|
19
|
+
"base200",
|
|
20
|
+
"base300",
|
|
21
|
+
"baseContent",
|
|
22
|
+
"primary",
|
|
23
|
+
"primaryContent",
|
|
24
|
+
"secondary",
|
|
25
|
+
"secondaryContent",
|
|
26
|
+
"accent",
|
|
27
|
+
"accentContent",
|
|
28
|
+
"neutral",
|
|
29
|
+
"neutralContent"
|
|
30
|
+
];
|
|
31
|
+
/** A CSS color as theme files hold them: oklch(), hex, rgb() or hsl(). */
|
|
32
|
+
const cssColor = z.string().trim().max(80).regex(/^(#[0-9a-fA-F]{3,8}|(oklch|oklab|rgba?|hsla?)\([0-9.,%\s/+-]+\))$/, "Not a CSS color");
|
|
33
|
+
const payloadGetTheme = z.object({ action: z.literal("getTheme") });
|
|
34
|
+
const payloadSetTheme = z.object({
|
|
35
|
+
action: z.literal("setTheme"),
|
|
36
|
+
colors: z.partialRecord(z.enum(EDITABLE_THEME_COLORS), cssColor)
|
|
37
|
+
});
|
|
38
|
+
/** Mockup token (Tailwind config of a mockup) → theme color. */
|
|
39
|
+
const MOCKUP_TOKENS = {
|
|
40
|
+
"base-100": "base100",
|
|
41
|
+
"base-200": "base200",
|
|
42
|
+
"base-300": "base300",
|
|
43
|
+
"base-content": "baseContent",
|
|
44
|
+
primary: "primary",
|
|
45
|
+
"primary-content": "primaryContent",
|
|
46
|
+
secondary: "secondary",
|
|
47
|
+
"secondary-content": "secondaryContent",
|
|
48
|
+
accent: "accent",
|
|
49
|
+
"accent-content": "accentContent",
|
|
50
|
+
neutral: "neutral",
|
|
51
|
+
"neutral-content": "neutralContent"
|
|
52
|
+
};
|
|
53
|
+
/** The colors a mockup sets in its Tailwind config, in the theme's naming. */
|
|
54
|
+
function mockupThemeColors(html) {
|
|
55
|
+
const script = [...html.matchAll(/<script\b[^>]*>([\s\S]*?)<\/script>/g)].find((m) => m[1].includes("tailwind.config"))?.[1];
|
|
56
|
+
const colors = {};
|
|
57
|
+
if (!script) return colors;
|
|
58
|
+
for (const [token, key] of Object.entries(MOCKUP_TOKENS)) {
|
|
59
|
+
const value = new RegExp(`['"]?${token.replace(/-/g, "\\-")}['"]?\\s*:\\s*(['"])([^'"]*)\\1`).exec(script)?.[2];
|
|
60
|
+
if (value && cssColor.safeParse(value).success) colors[key] = value.trim();
|
|
61
|
+
}
|
|
62
|
+
return colors;
|
|
63
|
+
}
|
|
64
|
+
/** The block `setTheme` keeps up to date at the end of the brief. */
|
|
65
|
+
const BRIEF_START = "<!-- upstart:theme-colors -->";
|
|
66
|
+
const BRIEF_END = "<!-- /upstart:theme-colors -->";
|
|
67
|
+
function briefWithUserColors(brief, themeFile) {
|
|
68
|
+
const block = [
|
|
69
|
+
BRIEF_START,
|
|
70
|
+
"## Colors chosen by the user",
|
|
71
|
+
`The user set the site's colors themselves in the editor. \`${themeFile}\` is the source of truth for colors and wins over any palette above: keep its values, and use its tokens (bg-base-100, text-primary…) rather than new colors.`,
|
|
72
|
+
BRIEF_END
|
|
73
|
+
].join("\n");
|
|
74
|
+
const start = brief.indexOf(BRIEF_START);
|
|
75
|
+
const end = brief.indexOf(BRIEF_END);
|
|
76
|
+
if (start !== -1 && end > start) return `${brief.slice(0, start)}${block}${brief.slice(end + 30)}`;
|
|
77
|
+
return `${brief.trimEnd()}\n\n${block}\n`;
|
|
78
|
+
}
|
|
79
|
+
/** The theme file the site uses by default, from `app/config/site.json`. */
|
|
80
|
+
async function defaultThemeFile(projectRoot) {
|
|
81
|
+
try {
|
|
82
|
+
const site = JSON.parse(await fs.readFile(path.join(projectRoot, "app", "config", "site.json"), "utf-8"));
|
|
83
|
+
const themeId = site.themes?.[site.themes.default ?? "light"] ?? site.themes?.light;
|
|
84
|
+
if (!themeId || !/^[a-z0-9-]+$/.test(themeId)) return null;
|
|
85
|
+
return {
|
|
86
|
+
themeId,
|
|
87
|
+
filePath: path.join(projectRoot, "app", "config", "themes", `${themeId}.json`)
|
|
88
|
+
};
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** The mockup the brief names as its reference (`docs/mockups/<direction>/…`). */
|
|
94
|
+
async function chosenMockup(projectRoot) {
|
|
95
|
+
const brief = await fs.readFile(path.join(projectRoot, "docs", "DESIGN.md"), "utf-8").catch(() => "");
|
|
96
|
+
return /docs\/mockups\/([a-z0-9-]+)\//.exec(brief)?.[1] ?? null;
|
|
97
|
+
}
|
|
98
|
+
/** The palettes the client was offered for the chosen mockup; none for an older site. */
|
|
99
|
+
async function offeredPalettes(projectRoot) {
|
|
100
|
+
const direction = await chosenMockup(projectRoot);
|
|
101
|
+
if (!direction) return [];
|
|
102
|
+
const dir = path.join(projectRoot, "docs", "mockups", direction);
|
|
103
|
+
const readPalette = async (id, folder) => {
|
|
104
|
+
const colors = mockupThemeColors(await fs.readFile(path.join(folder, "index.html"), "utf-8").catch(() => ""));
|
|
105
|
+
if (Object.keys(colors).length < 3) return null;
|
|
106
|
+
return {
|
|
107
|
+
id,
|
|
108
|
+
label: (await fs.readFile(path.join(folder, "variant.json"), "utf-8").then((raw) => JSON.parse(raw)).catch(() => ({}))).palette ?? "",
|
|
109
|
+
colors
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
const variants = await fs.readdir(path.join(dir, "palettes")).catch(() => []);
|
|
113
|
+
return (await Promise.all([readPalette("current", dir), ...variants.filter((n) => /^\d+$/.test(n)).sort((a, b) => Number(a) - Number(b)).map((n) => readPalette(`palette-${n}`, path.join(dir, "palettes", n)))])).filter((p) => p !== null);
|
|
114
|
+
}
|
|
115
|
+
async function getSiteTheme(projectRoot) {
|
|
116
|
+
const theme = await defaultThemeFile(projectRoot);
|
|
117
|
+
if (!theme) return {
|
|
118
|
+
success: false,
|
|
119
|
+
error: "This site has no theme in app/config/site.json"
|
|
120
|
+
};
|
|
121
|
+
try {
|
|
122
|
+
const content = JSON.parse(await fs.readFile(theme.filePath, "utf-8"));
|
|
123
|
+
const colors = {};
|
|
124
|
+
for (const key of EDITABLE_THEME_COLORS) {
|
|
125
|
+
const value = content.colors?.[key];
|
|
126
|
+
if (typeof value === "string") colors[key] = value;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
themeId: theme.themeId,
|
|
131
|
+
colors,
|
|
132
|
+
palettes: await offeredPalettes(projectRoot)
|
|
133
|
+
};
|
|
134
|
+
} catch {
|
|
135
|
+
return {
|
|
136
|
+
success: false,
|
|
137
|
+
error: `The theme file app/config/themes/${theme.themeId}.json is missing`
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async function setSiteTheme(projectRoot, params) {
|
|
142
|
+
const parsed = payloadSetTheme.safeParse(params);
|
|
143
|
+
if (!parsed.success) return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: `Invalid payload: ${parsed.error.message}`
|
|
146
|
+
};
|
|
147
|
+
if (Object.keys(parsed.data.colors).length === 0) return {
|
|
148
|
+
success: true,
|
|
149
|
+
filePaths: []
|
|
150
|
+
};
|
|
151
|
+
const theme = await defaultThemeFile(projectRoot);
|
|
152
|
+
if (!theme) return {
|
|
153
|
+
success: false,
|
|
154
|
+
error: "This site has no theme in app/config/site.json"
|
|
155
|
+
};
|
|
156
|
+
try {
|
|
157
|
+
const content = JSON.parse(await fs.readFile(theme.filePath, "utf-8"));
|
|
158
|
+
content.colors = {
|
|
159
|
+
...content.colors,
|
|
160
|
+
...parsed.data.colors
|
|
161
|
+
};
|
|
162
|
+
await fs.writeFile(theme.filePath, `${JSON.stringify(content, null, 2)}\n`);
|
|
163
|
+
const filePaths = [theme.filePath];
|
|
164
|
+
const briefPath = path.join(projectRoot, "docs", "DESIGN.md");
|
|
165
|
+
const brief = await fs.readFile(briefPath, "utf-8").catch(() => null);
|
|
166
|
+
if (brief !== null) {
|
|
167
|
+
await fs.writeFile(briefPath, briefWithUserColors(brief, path.relative(projectRoot, theme.filePath)));
|
|
168
|
+
filePaths.push(briefPath);
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
success: true,
|
|
172
|
+
filePaths
|
|
173
|
+
};
|
|
174
|
+
} catch (err) {
|
|
175
|
+
return {
|
|
176
|
+
success: false,
|
|
177
|
+
error: String(err)
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//#endregion
|
|
182
|
+
export { EDITABLE_THEME_COLORS, getSiteTheme, payloadGetTheme, payloadSetTheme, setSiteTheme };
|
|
183
|
+
|
|
184
|
+
//# sourceMappingURL=site-theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"site-theme.js","names":[],"sources":["../src/site-theme.ts"],"sourcesContent":["/**\n * The site's colors, editable from the editor's \"Colors\" panel.\n *\n * A site's colors live in its theme file (`app/config/themes/<id>.json`, the one `site.json` names\n * as the default), read by the theme plugin at build time. A new site also keeps the palettes the\n * client was offered when choosing its look (`docs/mockups/<direction>/` and its `palettes/<n>/`\n * variants, see the `PaletteVariants` tool): the panel offers them again, next to a free picker.\n *\n * Saving also records in `docs/DESIGN.md` that the user chose the colors themselves: agents read\n * the brief before any UI work, and would otherwise bring the brief's palette back.\n */\n\nimport fs from \"fs/promises\";\nimport path from \"path\";\nimport z from \"zod\";\n\n/** The theme colors the panel edits, in the theme file's naming. */\nexport const EDITABLE_THEME_COLORS = [\n \"base100\",\n \"base200\",\n \"base300\",\n \"baseContent\",\n \"primary\",\n \"primaryContent\",\n \"secondary\",\n \"secondaryContent\",\n \"accent\",\n \"accentContent\",\n \"neutral\",\n \"neutralContent\",\n] as const;\nexport type EditableThemeColor = (typeof EDITABLE_THEME_COLORS)[number];\nexport type ThemeColors = Partial<Record<EditableThemeColor, string>>;\n\n/** A CSS color as theme files hold them: oklch(), hex, rgb() or hsl(). */\nconst cssColor = z\n .string()\n .trim()\n .max(80)\n .regex(/^(#[0-9a-fA-F]{3,8}|(oklch|oklab|rgba?|hsla?)\\([0-9.,%\\s/+-]+\\))$/, \"Not a CSS color\");\n\nexport const payloadGetTheme = z.object({ action: z.literal(\"getTheme\") });\nexport type PayloadGetTheme = z.infer<typeof payloadGetTheme>;\n\nexport const payloadSetTheme = z.object({\n action: z.literal(\"setTheme\"),\n colors: z.partialRecord(z.enum(EDITABLE_THEME_COLORS), cssColor),\n});\nexport type PayloadSetTheme = z.infer<typeof payloadSetTheme>;\n\nexport interface ThemePalette {\n /** Stable id: \"current\" for the chosen mockup, \"palette-<n>\" for its variants. */\n id: string;\n /** The catalog palette's description, when known (e.g. \"Blush base, deep rose text…\"). */\n label: string;\n colors: ThemeColors;\n}\n\nexport type GetThemeResult =\n | { success: true; themeId: string; colors: ThemeColors; palettes: ThemePalette[] }\n | { success: false; error: string };\n\nexport type SetThemeResult = { success: true; filePaths: string[] } | { success: false; error: string };\n\n/** Mockup token (Tailwind config of a mockup) → theme color. */\nconst MOCKUP_TOKENS: Record<string, EditableThemeColor> = {\n \"base-100\": \"base100\",\n \"base-200\": \"base200\",\n \"base-300\": \"base300\",\n \"base-content\": \"baseContent\",\n primary: \"primary\",\n \"primary-content\": \"primaryContent\",\n secondary: \"secondary\",\n \"secondary-content\": \"secondaryContent\",\n accent: \"accent\",\n \"accent-content\": \"accentContent\",\n neutral: \"neutral\",\n \"neutral-content\": \"neutralContent\",\n};\n\n/** The colors a mockup sets in its Tailwind config, in the theme's naming. */\nexport function mockupThemeColors(html: string): ThemeColors {\n const script = [...html.matchAll(/<script\\b[^>]*>([\\s\\S]*?)<\\/script>/g)].find((m) =>\n m[1].includes(\"tailwind.config\"),\n )?.[1];\n const colors: ThemeColors = {};\n if (!script) return colors;\n for (const [token, key] of Object.entries(MOCKUP_TOKENS)) {\n const pattern = new RegExp(`['\"]?${token.replace(/-/g, \"\\\\-\")}['\"]?\\\\s*:\\\\s*(['\"])([^'\"]*)\\\\1`);\n const value = pattern.exec(script)?.[2];\n if (value && cssColor.safeParse(value).success) colors[key] = value.trim();\n }\n return colors;\n}\n\n/** The block `setTheme` keeps up to date at the end of the brief. */\nconst BRIEF_START = \"<!-- upstart:theme-colors -->\";\nconst BRIEF_END = \"<!-- /upstart:theme-colors -->\";\n\nexport function briefWithUserColors(brief: string, themeFile: string): string {\n const block = [\n BRIEF_START,\n \"## Colors chosen by the user\",\n `The user set the site's colors themselves in the editor. \\`${themeFile}\\` is the source of truth for colors and wins over any palette above: keep its values, and use its tokens (bg-base-100, text-primary…) rather than new colors.`,\n BRIEF_END,\n ].join(\"\\n\");\n const start = brief.indexOf(BRIEF_START);\n const end = brief.indexOf(BRIEF_END);\n if (start !== -1 && end > start) {\n return `${brief.slice(0, start)}${block}${brief.slice(end + BRIEF_END.length)}`;\n }\n return `${brief.trimEnd()}\\n\\n${block}\\n`;\n}\n\n/** The theme file the site uses by default, from `app/config/site.json`. */\nasync function defaultThemeFile(projectRoot: string): Promise<{ themeId: string; filePath: string } | null> {\n try {\n const site = JSON.parse(\n await fs.readFile(path.join(projectRoot, \"app\", \"config\", \"site.json\"), \"utf-8\"),\n ) as {\n themes?: { light?: string; dark?: string; default?: \"light\" | \"dark\" };\n };\n const themeId = site.themes?.[site.themes.default ?? \"light\"] ?? site.themes?.light;\n if (!themeId || !/^[a-z0-9-]+$/.test(themeId)) return null;\n return { themeId, filePath: path.join(projectRoot, \"app\", \"config\", \"themes\", `${themeId}.json`) };\n } catch {\n return null;\n }\n}\n\n/** The mockup the brief names as its reference (`docs/mockups/<direction>/…`). */\nasync function chosenMockup(projectRoot: string): Promise<string | null> {\n const brief = await fs.readFile(path.join(projectRoot, \"docs\", \"DESIGN.md\"), \"utf-8\").catch(() => \"\");\n return /docs\\/mockups\\/([a-z0-9-]+)\\//.exec(brief)?.[1] ?? null;\n}\n\n/** The palettes the client was offered for the chosen mockup; none for an older site. */\nexport async function offeredPalettes(projectRoot: string): Promise<ThemePalette[]> {\n const direction = await chosenMockup(projectRoot);\n if (!direction) return [];\n const dir = path.join(projectRoot, \"docs\", \"mockups\", direction);\n const readPalette = async (id: string, folder: string): Promise<ThemePalette | null> => {\n const html = await fs.readFile(path.join(folder, \"index.html\"), \"utf-8\").catch(() => \"\");\n const colors = mockupThemeColors(html);\n if (Object.keys(colors).length < 3) return null;\n const variant = await fs\n .readFile(path.join(folder, \"variant.json\"), \"utf-8\")\n .then((raw) => JSON.parse(raw) as { palette?: string })\n .catch((): { palette?: string } => ({}));\n return { id, label: variant.palette ?? \"\", colors };\n };\n const variants = await fs.readdir(path.join(dir, \"palettes\")).catch((): string[] => []);\n const palettes = await Promise.all([\n readPalette(\"current\", dir),\n ...variants\n .filter((n) => /^\\d+$/.test(n))\n .sort((a, b) => Number(a) - Number(b))\n .map((n) => readPalette(`palette-${n}`, path.join(dir, \"palettes\", n))),\n ]);\n return palettes.filter((p): p is ThemePalette => p !== null);\n}\n\nexport async function getSiteTheme(projectRoot: string): Promise<GetThemeResult> {\n const theme = await defaultThemeFile(projectRoot);\n if (!theme) return { success: false, error: \"This site has no theme in app/config/site.json\" };\n try {\n const content = JSON.parse(await fs.readFile(theme.filePath, \"utf-8\")) as {\n colors?: Record<string, string>;\n };\n const colors: ThemeColors = {};\n for (const key of EDITABLE_THEME_COLORS) {\n const value = content.colors?.[key];\n if (typeof value === \"string\") colors[key] = value;\n }\n return { success: true, themeId: theme.themeId, colors, palettes: await offeredPalettes(projectRoot) };\n } catch {\n return { success: false, error: `The theme file app/config/themes/${theme.themeId}.json is missing` };\n }\n}\n\nexport async function setSiteTheme(projectRoot: string, params: PayloadSetTheme): Promise<SetThemeResult> {\n const parsed = payloadSetTheme.safeParse(params);\n if (!parsed.success) return { success: false, error: `Invalid payload: ${parsed.error.message}` };\n if (Object.keys(parsed.data.colors).length === 0) return { success: true, filePaths: [] };\n const theme = await defaultThemeFile(projectRoot);\n if (!theme) return { success: false, error: \"This site has no theme in app/config/site.json\" };\n try {\n const content = JSON.parse(await fs.readFile(theme.filePath, \"utf-8\")) as {\n colors?: Record<string, string>;\n };\n content.colors = { ...content.colors, ...parsed.data.colors };\n await fs.writeFile(theme.filePath, `${JSON.stringify(content, null, 2)}\\n`);\n const filePaths = [theme.filePath];\n\n const briefPath = path.join(projectRoot, \"docs\", \"DESIGN.md\");\n const brief = await fs.readFile(briefPath, \"utf-8\").catch(() => null);\n if (brief !== null) {\n await fs.writeFile(briefPath, briefWithUserColors(brief, path.relative(projectRoot, theme.filePath)));\n filePaths.push(briefPath);\n }\n return { success: true, filePaths };\n } catch (err) {\n return { success: false, error: String(err) };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EditableEntry } from "./vite-plugin-upstart-attrs.js";
|
|
2
|
+
import { EDITABLE_THEME_COLORS, EditableThemeColor, GetThemeResult, PayloadGetTheme, PayloadSetTheme, SetThemeResult, ThemeColors, ThemePalette, payloadGetTheme, payloadSetTheme } from "./site-theme.js";
|
|
2
3
|
import z from "zod";
|
|
3
4
|
|
|
4
5
|
//#region src/upstart-editor-api.d.ts
|
|
@@ -243,6 +244,10 @@ declare class UpstartEditorAPI {
|
|
|
243
244
|
* workspace by the caller, which has bucket access.
|
|
244
245
|
*/
|
|
245
246
|
setSiteMeta(params: PayloadSetSiteMeta): Promise<SetSiteMetaResult>;
|
|
247
|
+
/** The site's colors and, for a new site, the palettes offered with its mockup (see site-theme.ts). */
|
|
248
|
+
getTheme(_params: PayloadGetTheme): Promise<GetThemeResult>;
|
|
249
|
+
/** Write colors to the site's theme file, and record in the brief that the user chose them. */
|
|
250
|
+
setTheme(params: PayloadSetTheme): Promise<SetThemeResult>;
|
|
246
251
|
private readSiteConfig;
|
|
247
252
|
private readRootFile;
|
|
248
253
|
private setMetaExport;
|
|
@@ -278,5 +283,5 @@ declare class UpstartEditorAPI {
|
|
|
278
283
|
getElementsByFile(file: string): Record<string, EditableEntry>;
|
|
279
284
|
}
|
|
280
285
|
//#endregion
|
|
281
|
-
export { EditResult, EditableRegistry, GetPageMetaResult, GetSiteMetaResult, PageMetaField, PayloadArrayItemAdd, PayloadArrayItemDelete, PayloadArraySet, PayloadEditClassName, PayloadEditImage, PayloadEditText, PayloadEditTextDirect, PayloadGetPageMeta, PayloadGetSiteMeta, PayloadSetPageMeta, PayloadSetSiteMeta, SetPageMetaResult, SetSiteMetaResult, UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect, payloadGetPageMeta, payloadGetSiteMeta, payloadSetPageMeta, payloadSetSiteMeta, readLocaleValue, writeLocaleValue };
|
|
286
|
+
export { EDITABLE_THEME_COLORS, EditResult, EditableRegistry, type EditableThemeColor, GetPageMetaResult, GetSiteMetaResult, type GetThemeResult, PageMetaField, PayloadArrayItemAdd, PayloadArrayItemDelete, PayloadArraySet, PayloadEditClassName, PayloadEditImage, PayloadEditText, PayloadEditTextDirect, PayloadGetPageMeta, PayloadGetSiteMeta, type PayloadGetTheme, PayloadSetPageMeta, PayloadSetSiteMeta, type PayloadSetTheme, SetPageMetaResult, SetSiteMetaResult, type SetThemeResult, type ThemeColors, type ThemePalette, UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect, payloadGetPageMeta, payloadGetSiteMeta, payloadGetTheme, payloadSetPageMeta, payloadSetSiteMeta, payloadSetTheme, readLocaleValue, writeLocaleValue };
|
|
282
287
|
//# sourceMappingURL=upstart-editor-api.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"upstart-editor-api.d.ts","names":[],"sources":["../src/upstart-editor-api.ts"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { analyzeRouteMeta } from "./page-meta.js";
|
|
2
2
|
import { ensureRootSiteMeta } from "./site-meta.js";
|
|
3
|
+
import { EDITABLE_THEME_COLORS, getSiteTheme, payloadGetTheme, payloadSetTheme, setSiteTheme } from "./site-theme.js";
|
|
3
4
|
import MagicString from "magic-string";
|
|
4
5
|
import { parseSync } from "oxc-parser";
|
|
5
6
|
import fs from "node:fs/promises";
|
|
@@ -1012,6 +1013,14 @@ var UpstartEditorAPI = class {
|
|
|
1012
1013
|
};
|
|
1013
1014
|
}
|
|
1014
1015
|
}
|
|
1016
|
+
/** The site's colors and, for a new site, the palettes offered with its mockup (see site-theme.ts). */
|
|
1017
|
+
async getTheme(_params) {
|
|
1018
|
+
return getSiteTheme(this.projectRoot);
|
|
1019
|
+
}
|
|
1020
|
+
/** Write colors to the site's theme file, and record in the brief that the user chose them. */
|
|
1021
|
+
async setTheme(params) {
|
|
1022
|
+
return setSiteTheme(this.projectRoot, params);
|
|
1023
|
+
}
|
|
1015
1024
|
async readSiteConfig() {
|
|
1016
1025
|
try {
|
|
1017
1026
|
const raw = await fs.readFile(path.join(this.projectRoot, "app", "config", "site.json"), "utf-8");
|
|
@@ -1342,6 +1351,6 @@ var UpstartEditorAPI = class {
|
|
|
1342
1351
|
}
|
|
1343
1352
|
};
|
|
1344
1353
|
//#endregion
|
|
1345
|
-
export { UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect, payloadGetPageMeta, payloadGetSiteMeta, payloadSetPageMeta, payloadSetSiteMeta, readLocaleValue, writeLocaleValue };
|
|
1354
|
+
export { EDITABLE_THEME_COLORS, UpstartEditorAPI, escapeStringLiteralBody, payloadArrayItemAdd, payloadArrayItemDelete, payloadArraySet, payloadEditClassName, payloadEditImage, payloadEditText, payloadEditTextDirect, payloadGetPageMeta, payloadGetSiteMeta, payloadGetTheme, payloadSetPageMeta, payloadSetSiteMeta, payloadSetTheme, readLocaleValue, writeLocaleValue };
|
|
1346
1355
|
|
|
1347
1356
|
//# sourceMappingURL=upstart-editor-api.js.map
|