lildocs 0.1.2 → 0.1.3

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/cli.mjs CHANGED
@@ -942,6 +942,7 @@ async function exists$1(filePath) {
942
942
  //#region src/core/theme.ts
943
943
  const MIN_BACKGROUND_CONTRAST = 1.08;
944
944
  const PREFERRED_BACKGROUND_CONTRAST = 1.14;
945
+ const MIN_DARK_BORDER_CONTRAST = 1.35;
945
946
  const MAX_BACKGROUND_CONTRAST_STEPS = 12;
946
947
  const BACKGROUND_LIGHTNESS_STEP = .015;
947
948
  const MAX_BACKGROUND_CHROMA = .03;
@@ -956,6 +957,7 @@ const themes = {
956
957
  border: "#e5e5e5",
957
958
  link: "#2563eb",
958
959
  codeBackground: "#f6f8fa",
960
+ codeForeground: "#24292e",
959
961
  sidebarBackground: "#fafafa"
960
962
  },
961
963
  font: {
@@ -963,7 +965,8 @@ const themes = {
963
965
  body: "system-ui, sans-serif",
964
966
  code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
965
967
  },
966
- shiki: { theme: "github-light" }
968
+ shiki: { theme: "github-light" },
969
+ background: { gradient: "linear-gradient(180deg, rgb(37 99 235 / 0.045), transparent 220px)" }
967
970
  },
968
971
  minimal: {
969
972
  color: {
@@ -973,6 +976,7 @@ const themes = {
973
976
  border: "#dddddd",
974
977
  link: "#0f766e",
975
978
  codeBackground: "#f7f7f7",
979
+ codeForeground: "#24292e",
976
980
  sidebarBackground: "#ffffff"
977
981
  },
978
982
  font: {
@@ -980,7 +984,8 @@ const themes = {
980
984
  body: "Arial, sans-serif",
981
985
  code: "Menlo, Consolas, monospace"
982
986
  },
983
- shiki: { theme: "github-light" }
987
+ shiki: { theme: "github-light" },
988
+ background: { gradient: "linear-gradient(180deg, rgb(15 118 110 / 0.04), transparent 220px)" }
984
989
  }
985
990
  };
986
991
  async function resolveTheme(options) {
@@ -1056,10 +1061,11 @@ function themeToCssVariables(theme, fontOverrides = {}, linkOptions = {}, naviga
1056
1061
  const navigationDuration = navigationOptions.duration ?? 180;
1057
1062
  const rootVariables = themeToCssVariableBlock(theme.light, fontOverrides);
1058
1063
  const darkVariables = theme.dark ? themeToCssVariableBlock(theme.dark, fontOverrides) : void 0;
1064
+ const rootBackgroundVariables = themeToBackgroundVariableBlock(theme.light);
1065
+ const darkBackgroundVariables = theme.dark ? themeToBackgroundVariableBlock(theme.dark) : void 0;
1059
1066
  return `:root {
1060
1067
  ${theme.dark ? " color-scheme: light dark;\n" : ""}${rootVariables}
1061
- --ld-background-image: none;
1062
- --ld-background-blend-mode: normal;
1068
+ ${rootBackgroundVariables}
1063
1069
  --ld-font-logo: var(--ld-font-heading);
1064
1070
  --ld-link-text-decoration: ${linkDecoration.default};
1065
1071
  --ld-link-hover-text-decoration: ${linkDecoration.hover};
@@ -1069,7 +1075,7 @@ ${theme.dark ? " color-scheme: light dark;\n" : ""}${rootVariables}
1069
1075
 
1070
1076
  @media (prefers-color-scheme: dark) {
1071
1077
  :root {
1072
- ${darkVariables} color-scheme: dark;
1078
+ ${darkVariables}${darkBackgroundVariables} color-scheme: dark;
1073
1079
  }
1074
1080
  }` : ""}`;
1075
1081
  }
@@ -1112,12 +1118,18 @@ function themeToCssVariableBlock(theme, fontOverrides) {
1112
1118
  --ld-color-border: ${color.border};
1113
1119
  --ld-color-link: ${color.link};
1114
1120
  --ld-color-code-background: ${color.codeBackground};
1121
+ --ld-color-code-foreground: ${color.codeForeground ?? color.text};
1115
1122
  --ld-color-sidebar-background: ${color.sidebarBackground ?? color.background};
1116
1123
  --ld-font-heading: ${fonts.heading};
1117
1124
  --ld-font-body: ${fonts.body};
1118
1125
  --ld-font-code: ${fonts.code};
1119
1126
  `;
1120
1127
  }
1128
+ function themeToBackgroundVariableBlock(theme) {
1129
+ return ` --ld-background-image: ${theme.background?.gradient ?? "none"};
1130
+ --ld-background-blend-mode: ${theme.background?.blendMode ?? "normal"};
1131
+ `;
1132
+ }
1121
1133
  function normalizeThemeColors(color) {
1122
1134
  return {
1123
1135
  ...color,
@@ -1141,6 +1153,11 @@ function validateTheme(value, themePath) {
1141
1153
  theme.font?.body,
1142
1154
  theme.font?.code ?? theme.font?.mono
1143
1155
  ].some((item) => typeof item !== "string" || item.length === 0)) throw new LildocsError(`Local theme is missing required color or font string values: ${themePath}`);
1156
+ if (theme.background !== void 0) {
1157
+ if (!theme.background || typeof theme.background !== "object") throw new LildocsError(`Local theme "background" must be an object: ${themePath}`);
1158
+ if (theme.background.gradient !== void 0 && (typeof theme.background.gradient !== "string" || theme.background.gradient.length === 0)) throw new LildocsError(`Local theme "background.gradient" must be a non-empty string: ${themePath}`);
1159
+ if (theme.background.blendMode !== void 0 && (typeof theme.background.blendMode !== "string" || theme.background.blendMode.length === 0)) throw new LildocsError(`Local theme "background.blendMode" must be a non-empty string: ${themePath}`);
1160
+ }
1144
1161
  return theme;
1145
1162
  }
1146
1163
  function mapShikiThemeToTheme(shikiTheme, themeName) {
@@ -1156,27 +1173,30 @@ function mapShikiThemeToTheme(shikiTheme, themeName) {
1156
1173
  "foreground",
1157
1174
  "sideBar.foreground"
1158
1175
  ], shikiTheme.fg ?? (shikiTheme.type === "dark" ? "#f5f5f5" : "#111111"));
1176
+ const mutedText = pickColor(colors, [
1177
+ "descriptionForeground",
1178
+ "breadcrumb.foreground",
1179
+ "editorLineNumber.foreground",
1180
+ "sideBar.foreground"
1181
+ ], tokenForeground(tokenColors, "comment") ?? text);
1182
+ const border = pickColor(colors, [
1183
+ "panel.border",
1184
+ "sideBar.border",
1185
+ "editorGroup.border",
1186
+ "tab.border"
1187
+ ], shikiTheme.type === "dark" ? "#333333" : "#e5e5e5");
1188
+ const link = pickColor(colors, [
1189
+ "textLink.foreground",
1190
+ "terminal.ansiBlue",
1191
+ "activityBarBadge.background"
1192
+ ], tokenForeground(tokenColors, "markup.inline.raw") ?? (shikiTheme.type === "dark" ? "#79b8ff" : "#2563eb"));
1159
1193
  return {
1160
1194
  color: {
1161
1195
  background,
1162
1196
  text,
1163
- mutedText: pickColor(colors, [
1164
- "descriptionForeground",
1165
- "breadcrumb.foreground",
1166
- "editorLineNumber.foreground",
1167
- "sideBar.foreground"
1168
- ], tokenForeground(tokenColors, "comment") ?? text),
1169
- border: pickColor(colors, [
1170
- "panel.border",
1171
- "sideBar.border",
1172
- "editorGroup.border",
1173
- "tab.border"
1174
- ], shikiTheme.type === "dark" ? "#333333" : "#e5e5e5"),
1175
- link: pickColor(colors, [
1176
- "textLink.foreground",
1177
- "terminal.ansiBlue",
1178
- "activityBarBadge.background"
1179
- ], tokenForeground(tokenColors, "markup.inline.raw") ?? (shikiTheme.type === "dark" ? "#79b8ff" : "#2563eb")),
1197
+ mutedText,
1198
+ border,
1199
+ link,
1180
1200
  codeBackground: ensureBackgroundContrast({
1181
1201
  background,
1182
1202
  candidate: pickColor(colors, [
@@ -1187,6 +1207,7 @@ function mapShikiThemeToTheme(shikiTheme, themeName) {
1187
1207
  ], background),
1188
1208
  isDark: shikiTheme.type === "dark"
1189
1209
  }),
1210
+ codeForeground: text,
1190
1211
  sidebarBackground: pickColor(colors, [
1191
1212
  "sideBar.background",
1192
1213
  "activityBar.background",
@@ -1198,7 +1219,8 @@ function mapShikiThemeToTheme(shikiTheme, themeName) {
1198
1219
  body: "system-ui, sans-serif",
1199
1220
  code: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace"
1200
1221
  },
1201
- shiki: { theme: themeName }
1222
+ shiki: { theme: themeName },
1223
+ background: { gradient: shikiTheme.type === "dark" ? `linear-gradient(180deg, color-mix(in srgb, ${link} 16%, transparent), transparent 240px)` : `linear-gradient(180deg, color-mix(in srgb, ${link} 9%, transparent), transparent 220px)` }
1202
1224
  };
1203
1225
  }
1204
1226
  function pickColor(colors, keys, fallback) {
@@ -1285,9 +1307,11 @@ function ensureDarkBorderLightness(options) {
1285
1307
  const textOklch = toOklch(text);
1286
1308
  const candidateOklch = toOklch(candidate);
1287
1309
  if (!backgroundOklch || !textOklch || !candidateOklch || typeof backgroundOklch.l !== "number" || typeof textOklch.l !== "number" || typeof candidateOklch.l !== "number") return options.candidate;
1288
- if (backgroundOklch.l >= textOklch.l || candidateOklch.l > backgroundOklch.l) return options.candidate;
1310
+ if (backgroundOklch.l >= textOklch.l) return options.candidate;
1311
+ const currentContrast = wcagContrast(background, candidate);
1312
+ if (candidateOklch.l > backgroundOklch.l && currentContrast >= MIN_DARK_BORDER_CONTRAST) return options.candidate;
1289
1313
  let bestColor = options.candidate;
1290
- let bestLightness = candidateOklch.l;
1314
+ let bestContrast = currentContrast;
1291
1315
  for (let step = 1; step <= MAX_BACKGROUND_CONTRAST_STEPS; step += 1) {
1292
1316
  const clampedColor = clampToRgb({
1293
1317
  ...candidateOklch,
@@ -1297,13 +1321,15 @@ function ensureDarkBorderLightness(options) {
1297
1321
  if (!clampedColor) continue;
1298
1322
  const adjustedColor = formatHex(clampedColor);
1299
1323
  const adjustedParsedColor = parse(adjustedColor);
1300
- const adjustedOklch = adjustedParsedColor ? toOklch(adjustedParsedColor) : void 0;
1324
+ if (!adjustedParsedColor) continue;
1325
+ const adjustedOklch = toOklch(adjustedParsedColor);
1301
1326
  if (!adjustedOklch || typeof adjustedOklch.l !== "number") continue;
1302
- if (adjustedOklch.l > bestLightness) {
1327
+ const adjustedContrast = wcagContrast(background, adjustedParsedColor);
1328
+ if (adjustedOklch.l > backgroundOklch.l && adjustedContrast > bestContrast) {
1303
1329
  bestColor = adjustedColor;
1304
- bestLightness = adjustedOklch.l;
1330
+ bestContrast = adjustedContrast;
1305
1331
  }
1306
- if (adjustedOklch.l > backgroundOklch.l) return adjustedColor;
1332
+ if (adjustedOklch.l > backgroundOklch.l && adjustedContrast >= MIN_DARK_BORDER_CONTRAST) return adjustedColor;
1307
1333
  }
1308
1334
  return bestColor;
1309
1335
  }
@@ -15,11 +15,13 @@
15
15
  }
16
16
 
17
17
  for (const block of blocks) {
18
- if (block.querySelector(":scope > .copyCodeButton")) {
18
+ if (block.parentElement?.classList.contains("copyCodeBlock")) {
19
19
  continue;
20
20
  }
21
21
 
22
22
  const copyText = block.textContent ?? "";
23
+ const wrapper = document.createElement("div");
24
+ wrapper.className = "copyCodeBlock";
23
25
  const button = document.createElement("button");
24
26
  button.type = "button";
25
27
  button.className = "copyCodeButton";
@@ -45,7 +47,8 @@
45
47
  }
46
48
  });
47
49
 
48
- block.append(button);
50
+ block.before(wrapper);
51
+ wrapper.append(block, button);
49
52
  }
50
53
  }
51
54
  })();
@@ -132,7 +132,7 @@ html.is-animating .transition-scale {
132
132
 
133
133
  .content {
134
134
  min-width: 0;
135
- padding-block: 28px 64px;
135
+ padding-block: 0 64px;
136
136
  line-height: 1.65;
137
137
  }
138
138
 
@@ -154,6 +154,10 @@ html.is-animating .transition-scale {
154
154
  margin-bottom: 8px;
155
155
  }
156
156
 
157
+ .content article > :first-child {
158
+ margin-top: 0;
159
+ }
160
+
157
161
  .content blockquote {
158
162
  margin: 20px 0;
159
163
  padding: 10px 16px;
@@ -181,7 +185,6 @@ html.is-animating .transition-scale {
181
185
  }
182
186
 
183
187
  .content a,
184
- .toc a,
185
188
  .navList a {
186
189
  color: var(--ld-color-link);
187
190
  }
@@ -279,6 +282,15 @@ html.is-animating .transition-scale {
279
282
  white-space: inherit;
280
283
  }
281
284
 
285
+ .content .copyCodeBlock {
286
+ position: relative;
287
+ margin-block: 1em;
288
+ }
289
+
290
+ .content .copyCodeBlock > pre {
291
+ margin: 0;
292
+ }
293
+
282
294
  .content pre.shiki {
283
295
  color: var(--shiki-light);
284
296
  background: var(--shiki-light-bg);
@@ -310,6 +322,13 @@ html.is-animating .transition-scale {
310
322
  font-size: 0.92em;
311
323
  }
312
324
 
325
+ .content :not(pre) > code {
326
+ padding: 0.16em 0.36em;
327
+ border-radius: 4px;
328
+ background: var(--ld-color-code-background);
329
+ color: var(--ld-color-code-foreground);
330
+ }
331
+
313
332
  .copyCodeButton {
314
333
  position: absolute;
315
334
  top: 10px;
@@ -325,10 +344,19 @@ html.is-animating .transition-scale {
325
344
  background: color-mix(in srgb, var(--ld-color-background) 88%, transparent);
326
345
  color: var(--ld-color-muted-text);
327
346
  line-height: 1;
347
+ opacity: 0;
348
+ pointer-events: none;
349
+ transition: opacity 120ms ease;
328
350
  vertical-align: top;
329
351
  cursor: pointer;
330
352
  }
331
353
 
354
+ .content .copyCodeBlock:hover .copyCodeButton,
355
+ .copyCodeButton:focus-visible {
356
+ opacity: 1;
357
+ pointer-events: auto;
358
+ }
359
+
332
360
  .copyCodeIcon {
333
361
  font-size: 18px;
334
362
  line-height: 1;
@@ -464,6 +492,7 @@ html.is-animating .transition-scale {
464
492
  }
465
493
 
466
494
  .toc a {
495
+ color: var(--ld-color-muted-text);
467
496
  text-decoration: none;
468
497
  }
469
498
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lildocs",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "A lightweight CLI that turns Markdown docs into a static searchable documentation site.",
5
5
  "homepage": "https://aleclarson.github.io/lildocs/",
6
6
  "repository": {