@lupinum/ginko-docs 0.3.0-rc.5 → 0.3.0
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/README.md +2 -2
- package/app/app.config.ts +1 -0
- package/app/assets/css/prose.css +33 -1
- package/app/components/site/ModeToggle.vue +55 -106
- package/app/components/site/SiteHeader.vue +182 -195
- package/app/components/site/SiteHeroCode.vue +12 -3
- package/app/components/site/SiteInteractionLayer.vue +9 -2
- package/app/components/site/SiteLocaleSwitcher.vue +28 -122
- package/app/components/site/SiteSocialLinks.vue +10 -16
- package/app/components/site/header-utils.ts +3 -0
- package/app/components/site/mode-toggle.utils.ts +18 -0
- package/app/features/search/useCommandCenter.ts +1 -9
- package/app/plugins/ginko-docs-theme.ts +1 -0
- package/i18n/messages/global/nav.ts +5 -0
- package/i18n/messages/global/theme.ts +2 -0
- package/icon-bundle.ts +2 -0
- package/index.d.ts +18 -0
- package/modules/syntax-highlighting.ts +120 -0
- package/nuxt.config.ts +2 -1
- package/package.json +3 -2
- package/shared/types/app-config.ts +2 -0
- package/shared/types/nuxt-config.ts +14 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
15
|
> [!WARNING]
|
|
16
|
-
> This package is
|
|
16
|
+
> This package is stable in the 0.3 release line. Because it is pre-1.0, a later minor release can contain breaking changes.
|
|
17
17
|
|
|
18
18
|
## Purpose
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@ Use this package when a Nuxt application needs documentation routes, navigation,
|
|
|
29
29
|
## Installation
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
pnpm add -D @lupinum/ginko-docs@0.3.0
|
|
32
|
+
pnpm add -D @lupinum/ginko-docs@0.3.0 @lupinum/ginko-content@0.4.0-rc.2
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
```ts
|
package/app/app.config.ts
CHANGED
package/app/assets/css/prose.css
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
--content-prose-heading-foreground: color-mix(in oklab, var(--foreground) 90%, transparent);
|
|
6
6
|
/* Inline code belongs to its surrounding surface; monospace type carries
|
|
7
7
|
the distinction without adding a nested chip background. */
|
|
8
|
-
--content-prose-inline-code-background:
|
|
8
|
+
--content-prose-inline-code-background: var(--background);
|
|
9
9
|
--content-prose-inline-code-foreground: var(--foreground);
|
|
10
10
|
--content-prose-code-highlight-border: var(--code-border);
|
|
11
11
|
/* Kept light enough that Shiki's light-plus tokens stay WCAG AA on top. */
|
|
@@ -303,6 +303,7 @@
|
|
|
303
303
|
border-radius: max(0.3125rem, calc(var(--radius) - 5px));
|
|
304
304
|
box-decoration-break: clone;
|
|
305
305
|
-webkit-box-decoration-break: clone;
|
|
306
|
+
border: 1px solid color-mix(in oklab, var(--foreground) 15%, transparent);
|
|
306
307
|
background-color: var(--content-prose-inline-code-background, var(--muted));
|
|
307
308
|
color: var(--content-prose-inline-code-foreground, var(--foreground));
|
|
308
309
|
/* em-based so chips scale with tables, callouts, and body text alike */
|
|
@@ -420,6 +421,37 @@ html.dark .shiki span {
|
|
|
420
421
|
text-decoration: var(--shiki-dark-text-decoration, inherit) !important;
|
|
421
422
|
}
|
|
422
423
|
|
|
424
|
+
/* Code blocks default to a stable dark presentation in either page theme.
|
|
425
|
+
"adaptive" leaves these local tokens unset and follows the page tokens. */
|
|
426
|
+
html[data-code-blocks="dark"] :where(.content-codeblock, .content-codegroup, .content-code-tree) {
|
|
427
|
+
--background: var(--theme-neutral-950);
|
|
428
|
+
--foreground: var(--theme-neutral-50);
|
|
429
|
+
--card: var(--theme-neutral-900);
|
|
430
|
+
--card-foreground: var(--theme-neutral-50);
|
|
431
|
+
--primary: var(--theme-primary-dark);
|
|
432
|
+
--primary-foreground: var(--theme-primary-dark-foreground);
|
|
433
|
+
--secondary: var(--theme-neutral-800);
|
|
434
|
+
--secondary-foreground: var(--theme-neutral-50);
|
|
435
|
+
--muted: var(--theme-neutral-800);
|
|
436
|
+
--muted-foreground: var(--theme-neutral-400);
|
|
437
|
+
--accent: var(--theme-neutral-800);
|
|
438
|
+
--accent-foreground: var(--theme-neutral-50);
|
|
439
|
+
--code: var(--theme-neutral-900);
|
|
440
|
+
--code-foreground: var(--theme-neutral-50);
|
|
441
|
+
--code-border: var(--theme-neutral-800);
|
|
442
|
+
--border: var(--theme-neutral-800);
|
|
443
|
+
--input: var(--theme-neutral-700);
|
|
444
|
+
--ring: var(--theme-primary-dark-ring);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
html[data-code-blocks="dark"] .content-codeblock.shiki span {
|
|
448
|
+
background-color: var(--shiki-dark-bg, transparent);
|
|
449
|
+
color: var(--shiki-dark) !important;
|
|
450
|
+
font-style: var(--shiki-dark-font-style, inherit) !important;
|
|
451
|
+
font-weight: var(--shiki-dark-font-weight, inherit) !important;
|
|
452
|
+
text-decoration: var(--shiki-dark-text-decoration, inherit) !important;
|
|
453
|
+
}
|
|
454
|
+
|
|
423
455
|
[data-fd-codeblock] {
|
|
424
456
|
background-color: var(--secondary);
|
|
425
457
|
color: var(--foreground);
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from "vue";
|
|
3
|
-
import { Button } from "#ginko-docs/components/ui/button";
|
|
4
3
|
import { computed } from "vue";
|
|
5
4
|
import { useColorMode, useI18n } from "#imports";
|
|
6
5
|
import { cn } from "#ginko-docs/utils";
|
|
6
|
+
import { headerUtilityButtonClass } from "#ginko-docs/components/site/header-utils";
|
|
7
7
|
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
DropdownMenuTrigger,
|
|
13
|
-
} from "#ginko-docs/components/ui/dropdown-menu";
|
|
8
|
+
nextColorModePreference,
|
|
9
|
+
themeToggleIcon,
|
|
10
|
+
themeToggleLabelKey,
|
|
11
|
+
} from "#ginko-docs/components/site/mode-toggle.utils";
|
|
14
12
|
|
|
15
13
|
const props = withDefaults(
|
|
16
14
|
defineProps<{
|
|
17
|
-
variant?: "icon" | "menu-row"
|
|
15
|
+
variant?: "icon" | "menu-row";
|
|
18
16
|
class?: HTMLAttributes["class"];
|
|
19
17
|
}>(),
|
|
20
18
|
{
|
|
@@ -25,108 +23,59 @@ const props = withDefaults(
|
|
|
25
23
|
const colorMode = useColorMode();
|
|
26
24
|
const { t } = useI18n();
|
|
27
25
|
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
{ value: "system", labelKey: "theme.system", icon: "lucide:monitor" },
|
|
32
|
-
] as const;
|
|
26
|
+
const isDark = computed(() => colorMode.value === "dark");
|
|
27
|
+
const toggleIcon = computed(() => themeToggleIcon(isDark.value));
|
|
28
|
+
const toggleLabel = computed(() => t(themeToggleLabelKey(isDark.value)));
|
|
33
29
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
() =>
|
|
38
|
-
colorModeOptions.find((option) => option.value === colorMode.preference) ??
|
|
39
|
-
colorModeOptions.find((option) => option.value === "system")!,
|
|
40
|
-
);
|
|
41
|
-
const triggerIcon = computed(() => selectedColorModeOption.value.icon);
|
|
42
|
-
const nextExplicitColorModeIcon = computed(() =>
|
|
43
|
-
colorMode.value === "dark" ? "lucide:sun" : "lucide:moon",
|
|
44
|
-
);
|
|
45
|
-
const triggerLabel = computed(() => t(selectedColorModeOption.value.labelKey));
|
|
46
|
-
|
|
47
|
-
function isColorModePreference(value: string): value is ColorModePreference {
|
|
48
|
-
return colorModeOptions.some((option) => option.value === value);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function setColorModePreference(value: unknown) {
|
|
52
|
-
if (typeof value === "string" && isColorModePreference(value)) {
|
|
53
|
-
colorMode.preference = value;
|
|
54
|
-
}
|
|
30
|
+
function toggleColorMode() {
|
|
31
|
+
const currentRendered = isDark.value ? "dark" : "light";
|
|
32
|
+
colorMode.preference = nextColorModePreference(currentRendered);
|
|
55
33
|
}
|
|
56
34
|
</script>
|
|
57
35
|
|
|
58
36
|
<template>
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
>
|
|
77
|
-
<span
|
|
78
|
-
class="flex shrink-0 items-center justify-center rounded-lg bg-muted/60 text-foreground"
|
|
79
|
-
:class="variant === 'menu-tile' ? 'size-8' : 'size-11'"
|
|
80
|
-
>
|
|
81
|
-
<Icon
|
|
82
|
-
:name="triggerIcon"
|
|
83
|
-
:class="variant === 'menu-tile' ? 'size-4' : 'size-5'"
|
|
84
|
-
aria-hidden="true"
|
|
85
|
-
/>
|
|
86
|
-
</span>
|
|
87
|
-
<span class="truncate">{{
|
|
88
|
-
variant === "menu-tile" ? triggerLabel : t("theme.label")
|
|
89
|
-
}}</span>
|
|
90
|
-
</span>
|
|
91
|
-
<Icon
|
|
92
|
-
:name="variant === 'menu-tile' ? 'lucide:chevron-down' : 'lucide:chevron-right'"
|
|
93
|
-
:class="variant === 'menu-tile' ? 'size-4' : 'size-5'"
|
|
94
|
-
class="text-muted-foreground"
|
|
95
|
-
aria-hidden="true"
|
|
96
|
-
/>
|
|
97
|
-
</Button>
|
|
98
|
-
<Button
|
|
99
|
-
v-else-if="variant === 'pill'"
|
|
100
|
-
variant="outline"
|
|
101
|
-
:class="cn('h-9 gap-2 rounded-full px-4 text-sm font-semibold', props.class)"
|
|
102
|
-
>
|
|
103
|
-
<Icon :name="triggerIcon" class="size-4" aria-hidden="true" />
|
|
104
|
-
<span>{{ triggerLabel }}</span>
|
|
105
|
-
<span class="sr-only">{{ t("theme.toggle") }}</span>
|
|
106
|
-
</Button>
|
|
107
|
-
<Button v-else variant="outline" size="icon" :class="cn('relative shrink-0', props.class)">
|
|
108
|
-
<Icon :name="nextExplicitColorModeIcon" class="size-[1.2rem]" aria-hidden="true" />
|
|
109
|
-
<span class="sr-only">{{ t("theme.toggle") }}</span>
|
|
110
|
-
</Button>
|
|
111
|
-
</DropdownMenuTrigger>
|
|
112
|
-
<DropdownMenuContent align="end" class="w-44" :portal-disabled="variant !== 'icon'">
|
|
113
|
-
<DropdownMenuRadioGroup
|
|
114
|
-
:model-value="colorMode.preference"
|
|
115
|
-
@update:model-value="setColorModePreference"
|
|
37
|
+
<button
|
|
38
|
+
v-if="variant === 'menu-row'"
|
|
39
|
+
type="button"
|
|
40
|
+
role="switch"
|
|
41
|
+
:aria-checked="isDark"
|
|
42
|
+
:aria-label="toggleLabel"
|
|
43
|
+
:class="
|
|
44
|
+
cn(
|
|
45
|
+
'flex h-14 w-full items-center justify-between rounded-none px-5 text-base font-semibold hover:bg-transparent',
|
|
46
|
+
props.class,
|
|
47
|
+
)
|
|
48
|
+
"
|
|
49
|
+
@click="toggleColorMode"
|
|
50
|
+
>
|
|
51
|
+
<span class="flex min-w-0 items-center gap-4">
|
|
52
|
+
<span
|
|
53
|
+
class="flex size-11 shrink-0 items-center justify-center rounded-lg bg-muted/60 text-foreground"
|
|
116
54
|
>
|
|
117
|
-
<
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
</
|
|
131
|
-
</
|
|
55
|
+
<Icon :name="toggleIcon" class="size-5" aria-hidden="true" />
|
|
56
|
+
</span>
|
|
57
|
+
<span class="truncate">{{ t("theme.label") }}</span>
|
|
58
|
+
</span>
|
|
59
|
+
<span
|
|
60
|
+
class="relative inline-flex h-6 w-11 shrink-0 rounded-full border border-border bg-muted/60 transition-colors"
|
|
61
|
+
:class="isDark ? 'bg-foreground/80' : ''"
|
|
62
|
+
aria-hidden="true"
|
|
63
|
+
>
|
|
64
|
+
<span
|
|
65
|
+
class="absolute top-0.5 size-5 rounded-full bg-background shadow-xs transition-transform motion-reduce:transition-none"
|
|
66
|
+
:class="isDark ? 'translate-x-[1.375rem]' : 'translate-x-0.5'"
|
|
67
|
+
/>
|
|
68
|
+
</span>
|
|
69
|
+
</button>
|
|
70
|
+
<button
|
|
71
|
+
v-else
|
|
72
|
+
type="button"
|
|
73
|
+
role="switch"
|
|
74
|
+
:aria-checked="isDark"
|
|
75
|
+
:aria-label="toggleLabel"
|
|
76
|
+
:class="cn(headerUtilityButtonClass, props.class)"
|
|
77
|
+
@click="toggleColorMode"
|
|
78
|
+
>
|
|
79
|
+
<Icon :name="toggleIcon" class="size-[18px]" aria-hidden="true" />
|
|
80
|
+
</button>
|
|
132
81
|
</template>
|