@terrafirmagreg-team/ui 0.1.0 → 1.2.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/package.json +14 -14
- package/src/components/blocks/button/icon-button.tsx +1 -0
- package/src/components/blocks/canvas/chevron-edge.tsx +126 -18
- package/src/components/blocks/canvas/flow-canvas.tsx +4 -0
- package/src/components/blocks/canvas/flow-toolbar.tsx +1 -0
- package/src/components/blocks/canvas/flow-viewport.tsx +1 -0
- package/src/components/blocks/fit-box.tsx +26 -0
- package/src/components/blocks/git/github-info.tsx +1 -0
- package/src/components/blocks/lang/content.tsx +40 -10
- package/src/components/blocks/lang/flag.tsx +1 -0
- package/src/components/blocks/lang/link.tsx +1 -0
- package/src/components/blocks/lang/redirect.tsx +2 -1
- package/src/components/blocks/lang/switcher.tsx +1 -0
- package/src/components/blocks/lang/untranslated-callout.tsx +4 -3
- package/src/components/blocks/lang/untranslated-chip.tsx +53 -0
- package/src/components/blocks/link/link-preview.tsx +2 -1
- package/src/components/blocks/list/row.tsx +1 -0
- package/src/components/blocks/media/glb/model.tsx +1 -0
- package/src/components/blocks/media/glb/scene.tsx +13 -12
- package/src/components/blocks/media/glb/viewer.tsx +1 -0
- package/src/components/blocks/media/image.tsx +3 -2
- package/src/components/blocks/media/slideshow.tsx +4 -3
- package/src/components/blocks/nav/breadcrumb.tsx +1 -0
- package/src/components/blocks/nav/copy-menu.tsx +37 -0
- package/src/components/blocks/nav/link.tsx +1 -0
- package/src/components/blocks/nav/site-menu.tsx +1 -0
- package/src/components/blocks/nav/site-title.tsx +1 -0
- package/src/components/blocks/nav/switcher.tsx +1 -0
- package/src/components/blocks/page/empty-state.tsx +1 -0
- package/src/components/blocks/page/heading.tsx +3 -2
- package/src/components/blocks/page/pager.tsx +110 -2
- package/src/components/blocks/readability/fields.tsx +250 -0
- package/src/components/blocks/readability/menu.tsx +74 -258
- package/src/components/blocks/readability/track.tsx +2 -1
- package/src/components/blocks/search/command.tsx +1 -0
- package/src/components/blocks/search/dialog.tsx +21 -9
- package/src/components/blocks/search/provider.tsx +2 -1
- package/src/components/blocks/sidebar.tsx +5 -5
- package/src/components/blocks/sprite.tsx +231 -0
- package/src/components/blocks/text/format-text.tsx +110 -0
- package/src/components/blocks/theme/toggle.tsx +4 -0
- package/src/components/blocks/tooltip/hint.tsx +1 -0
- package/src/components/blocks/tooltip/lazy.tsx +41 -0
- package/src/components/blocks/viewer/controls.tsx +11 -6
- package/src/components/blocks/viewer/float-bar.tsx +1 -0
- package/src/components/layouts/layout.tsx +2 -1
- package/src/components/layouts/left-sidebar/content.tsx +1 -0
- package/src/components/layouts/left-sidebar/context.tsx +1 -0
- package/src/components/layouts/left-sidebar/expand.tsx +2 -1
- package/src/components/layouts/left-sidebar/footer.tsx +10 -4
- package/src/components/layouts/left-sidebar/index.ts +1 -0
- package/src/components/layouts/left-sidebar/items.tsx +2 -1
- package/src/components/layouts/left-sidebar/search.tsx +4 -3
- package/src/components/layouts/left-sidebar/sidebar.tsx +3 -2
- package/src/components/layouts/left-sidebar/tree.tsx +1 -0
- package/src/components/layouts/right-sidebar.tsx +1 -0
- package/src/components/layouts/tree-context.tsx +1 -0
- package/src/components/mdx/accordion.tsx +3 -2
- package/src/components/mdx/banner.tsx +15 -12
- package/src/components/mdx/callout.tsx +1 -0
- package/src/components/mdx/card.tsx +12 -5
- package/src/components/mdx/codeblock.tsx +5 -4
- package/src/components/mdx/collapsible.tsx +1 -0
- package/src/components/mdx/files.tsx +1 -0
- package/src/components/mdx/glow-header.tsx +17 -3
- package/src/components/mdx/gradient-text.tsx +1 -0
- package/src/components/mdx/icon.tsx +35 -0
- package/src/components/mdx/image-zoom.tsx +5 -4
- package/src/components/mdx/math.tsx +30 -0
- package/src/components/mdx/mermaid.tsx +8 -1
- package/src/components/mdx/multiblock.tsx +1 -0
- package/src/components/mdx/preview.tsx +2 -1
- package/src/components/mdx/steps.tsx +1 -0
- package/src/components/mdx/table.tsx +1 -0
- package/src/components/mdx/tabs.tsx +2 -1
- package/src/components/mdx/type-table.tsx +7 -6
- package/src/{provider.tsx → components/provider.tsx} +1 -3
- package/src/hooks/canvas/use-flow-hover.ts +1 -0
- package/src/hooks/canvas/use-flow-viewport-persist.ts +1 -0
- package/src/hooks/page/use-fit-pages.ts +1 -0
- package/src/hooks/page/use-location-search.ts +8 -2
- package/src/hooks/page/use-page-preview.ts +2 -1
- package/src/hooks/page/use-paged-list.ts +1 -0
- package/src/hooks/readability/{provider.ts → use-readability.ts} +1 -0
- package/src/hooks/search/{dialog.ts → use-dialog.ts} +1 -0
- package/src/hooks/search/use-highlight-query.ts +8 -1
- package/src/hooks/search/{provider.ts → use-provider.ts} +1 -0
- package/src/hooks/use-async-value.ts +6 -3
- package/src/hooks/use-close-on-scroll.ts +1 -0
- package/src/hooks/use-copy-button.ts +1 -0
- package/src/hooks/use-cycle-index.ts +1 -0
- package/src/hooks/use-document-title.ts +1 -0
- package/src/hooks/use-element-size.ts +1 -0
- package/src/hooks/use-external-meta.ts +2 -1
- package/src/hooks/use-hot-key.ts +1 -0
- package/src/hooks/use-hover-follow.ts +1 -0
- package/src/hooks/use-in-view.ts +1 -0
- package/src/hooks/{lang.ts → use-lang.ts} +1 -0
- package/src/hooks/use-last-value.ts +1 -0
- package/src/hooks/{layout.ts → use-layout.ts} +1 -0
- package/src/hooks/use-marquee.ts +1 -0
- package/src/hooks/use-media-query.ts +1 -0
- package/src/hooks/use-mobile.ts +1 -0
- package/src/hooks/use-modifier-keys.ts +1 -0
- package/src/hooks/use-on-change.ts +1 -0
- package/src/hooks/use-pane-keys.ts +1 -0
- package/src/hooks/{sidebar.ts → use-sidebar.ts} +1 -0
- package/src/hooks/use-swipe.ts +1 -0
- package/src/hooks/use-tree.ts +1 -3
- package/src/hooks/viewer/use-in-float-bar.ts +1 -0
- package/src/hooks/viewer/use-viewer-prefs.ts +1 -0
- package/src/lib/canvas/edge.ts +78 -49
- package/src/lib/canvas/geometry.ts +1 -0
- package/src/lib/canvas/state.ts +1 -0
- package/src/lib/canvas/viewport.ts +1 -0
- package/src/lib/canvas/xyflow.ts +1 -0
- package/src/lib/config.ts +25 -0
- package/src/lib/mdx/callout.ts +1 -0
- package/src/lib/mdx/gradient.ts +1 -0
- package/src/lib/mdx/mermaid.ts +1 -0
- package/src/lib/media/crop-opaque.ts +1 -0
- package/src/lib/media/nearby-indexes.ts +1 -0
- package/src/lib/media/trim-transparent.ts +1 -0
- package/src/lib/readability/config.ts +1 -0
- package/src/lib/readability/spotlight.ts +1 -0
- package/src/lib/readability/store.ts +1 -0
- package/src/lib/tree.ts +1 -3
- package/src/lib/utils/cn.ts +1 -0
- package/src/lib/utils/format.ts +3 -3
- package/src/lib/utils/highlight-style.ts +10 -0
- package/src/lib/utils/history.ts +1 -0
- package/src/lib/utils/merge-refs.ts +1 -0
- package/src/lib/utils/nav-active.ts +1 -0
- package/src/lib/utils/stored-value.ts +1 -0
- package/src/lib/utils/urls.ts +2 -1
- package/src/locales/en_us.json +108 -117
- package/src/locales/pl_pl.json +33 -35
- package/src/locales/ru_ru.json +108 -117
- package/src/locales/uk_ua.json +33 -35
- package/src/locales/zh_cn.json +15 -17
- package/src/locales/zh_tw.json +15 -17
- package/src/locales.ts +1 -0
- package/src/types/canvas.ts +6 -1
- package/src/types/glb.ts +1 -0
- package/src/types/mdx-directives.ts +106 -0
- package/src/types/nav.ts +1 -0
- package/src/types/preview.ts +7 -1
- package/src/types/readability.ts +1 -0
- package/src/types/search.ts +1 -0
- package/src/types/tree.ts +1 -0
- package/src/types/viewer.ts +1 -0
- package/styles/content/base.css +198 -194
- package/styles/content/shiki.css +107 -108
- package/styles/content/typography.mjs +3637 -3941
- package/styles/content.css +5 -7
- package/styles.css +430 -413
- package/src/lib/utils/async-cache.ts +0 -32
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { usePathname } from 'next/navigation';
|
|
3
|
-
import {
|
|
3
|
+
import type { ComponentProps, ReactNode } from 'react';
|
|
4
4
|
import {
|
|
5
5
|
AlignStartVertical,
|
|
6
|
-
CircleHelp,
|
|
7
6
|
Columns2,
|
|
8
7
|
Flashlight,
|
|
9
8
|
FoldHorizontal,
|
|
@@ -16,9 +15,8 @@ import {
|
|
|
16
15
|
Settings,
|
|
17
16
|
UnfoldHorizontal,
|
|
18
17
|
} from 'lucide-react';
|
|
19
|
-
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
20
18
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
21
|
-
import { useReadability } from '@terrafirmagreg-team/ui/hooks/readability/
|
|
19
|
+
import { useReadability } from '@terrafirmagreg-team/ui/hooks/readability/use-readability';
|
|
22
20
|
import { readabilityConfig } from '@terrafirmagreg-team/ui/lib/readability/config';
|
|
23
21
|
import {
|
|
24
22
|
Popover,
|
|
@@ -26,24 +24,18 @@ import {
|
|
|
26
24
|
PopoverTrigger,
|
|
27
25
|
} from '@terrafirmagreg-team/ui/components/ui/popover';
|
|
28
26
|
import { IconButton } from '@terrafirmagreg-team/ui/components/blocks/button/icon-button';
|
|
29
|
-
import { Hint } from '@terrafirmagreg-team/ui/components/blocks/tooltip/hint';
|
|
30
|
-
import {
|
|
31
|
-
Field,
|
|
32
|
-
FieldContent,
|
|
33
|
-
FieldDescription,
|
|
34
|
-
FieldLabel,
|
|
35
|
-
} from '@terrafirmagreg-team/ui/components/ui/field';
|
|
36
|
-
import { Slider } from '@terrafirmagreg-team/ui/components/ui/slider';
|
|
37
|
-
import {
|
|
38
|
-
ToggleGroup,
|
|
39
|
-
ToggleGroupItem,
|
|
40
|
-
} from '@terrafirmagreg-team/ui/components/ui/toggle-group';
|
|
41
27
|
import {
|
|
42
28
|
readabilityLayouts,
|
|
43
29
|
spotlightStyles,
|
|
44
30
|
type ReadabilityLayout,
|
|
45
31
|
type SpotlightStyle,
|
|
46
32
|
} from '@terrafirmagreg-team/ui/types/readability';
|
|
33
|
+
import {
|
|
34
|
+
ReadabilityField,
|
|
35
|
+
ReadabilityHelp,
|
|
36
|
+
Segment,
|
|
37
|
+
WidthField,
|
|
38
|
+
} from './fields';
|
|
47
39
|
|
|
48
40
|
const layoutIcons: Record<ReadabilityLayout, ReactNode> = {
|
|
49
41
|
full: <Maximize2 />,
|
|
@@ -59,223 +51,6 @@ const styleIcons: Record<SpotlightStyle, ReactNode> = {
|
|
|
59
51
|
|
|
60
52
|
const switches = ['on', 'off'] as const;
|
|
61
53
|
|
|
62
|
-
function ReadabilityHelp({
|
|
63
|
-
title,
|
|
64
|
-
description,
|
|
65
|
-
items,
|
|
66
|
-
}: {
|
|
67
|
-
title: string;
|
|
68
|
-
description?: string;
|
|
69
|
-
items?: { icon?: ReactNode; label: string; help: string }[];
|
|
70
|
-
}) {
|
|
71
|
-
return (
|
|
72
|
-
<div className="space-y-2">
|
|
73
|
-
<div className="space-y-1">
|
|
74
|
-
<p className="font-medium">{title}</p>
|
|
75
|
-
{description && <FieldDescription className="text-xs">{description}</FieldDescription>}
|
|
76
|
-
</div>
|
|
77
|
-
{items?.map((item) => (
|
|
78
|
-
<div key={item.label} className="space-y-1 rounded-lg bg-muted/60 p-2.5">
|
|
79
|
-
<p className="flex items-center gap-1.5 text-xs font-medium [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:shrink-0">
|
|
80
|
-
{item.icon}
|
|
81
|
-
{item.label}
|
|
82
|
-
</p>
|
|
83
|
-
<FieldDescription className="text-xs">{item.help}</FieldDescription>
|
|
84
|
-
</div>
|
|
85
|
-
))}
|
|
86
|
-
</div>
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function ReadabilityField({
|
|
91
|
-
label,
|
|
92
|
-
icon,
|
|
93
|
-
help,
|
|
94
|
-
children,
|
|
95
|
-
}: {
|
|
96
|
-
label: ReactNode;
|
|
97
|
-
icon?: ReactNode;
|
|
98
|
-
help?: ReactNode;
|
|
99
|
-
children: ReactNode;
|
|
100
|
-
}) {
|
|
101
|
-
const [helpOpen, setHelpOpen] = useState(false);
|
|
102
|
-
return (
|
|
103
|
-
<Field className="gap-1.5">
|
|
104
|
-
<div className="flex items-center gap-1 [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:shrink-0">
|
|
105
|
-
{icon}
|
|
106
|
-
<FieldLabel className="flex-1 text-xs text-muted-foreground">{label}</FieldLabel>
|
|
107
|
-
{help && (
|
|
108
|
-
<Popover onOpenChange={setHelpOpen}>
|
|
109
|
-
<PopoverTrigger
|
|
110
|
-
render={
|
|
111
|
-
<IconButton
|
|
112
|
-
variant="ghost"
|
|
113
|
-
size="icon-xs"
|
|
114
|
-
tooltip={false}
|
|
115
|
-
label={typeof label === 'string' ? label : 'Help'}
|
|
116
|
-
>
|
|
117
|
-
<CircleHelp />
|
|
118
|
-
</IconButton>
|
|
119
|
-
}
|
|
120
|
-
/>
|
|
121
|
-
<PopoverContent className="w-56">{help}</PopoverContent>
|
|
122
|
-
</Popover>
|
|
123
|
-
)}
|
|
124
|
-
</div>
|
|
125
|
-
<FieldContent
|
|
126
|
-
className={cn(
|
|
127
|
-
'rounded-lg outline-2 outline-offset-4 outline-dashed outline-transparent transition-[outline-color] duration-200',
|
|
128
|
-
helpOpen && 'outline-primary',
|
|
129
|
-
)}
|
|
130
|
-
>
|
|
131
|
-
{children}
|
|
132
|
-
</FieldContent>
|
|
133
|
-
</Field>
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function ReadabilitySlider({
|
|
138
|
-
label,
|
|
139
|
-
icon,
|
|
140
|
-
help,
|
|
141
|
-
value,
|
|
142
|
-
onChange,
|
|
143
|
-
min = 0,
|
|
144
|
-
max = 100,
|
|
145
|
-
step = 1,
|
|
146
|
-
format = String,
|
|
147
|
-
disabled,
|
|
148
|
-
live,
|
|
149
|
-
}: {
|
|
150
|
-
label: ReactNode;
|
|
151
|
-
icon?: ReactNode;
|
|
152
|
-
help?: ReactNode;
|
|
153
|
-
value: number;
|
|
154
|
-
onChange: (value: number) => void;
|
|
155
|
-
min?: number;
|
|
156
|
-
max?: number;
|
|
157
|
-
step?: number;
|
|
158
|
-
format?: (value: number) => string;
|
|
159
|
-
disabled?: boolean;
|
|
160
|
-
live?: boolean;
|
|
161
|
-
}) {
|
|
162
|
-
const [draft, setDraft] = useState<{ key: number; value: number }>({ key: value, value });
|
|
163
|
-
const shown = draft.key === value ? draft.value : value;
|
|
164
|
-
return (
|
|
165
|
-
<ReadabilityField label={label} icon={icon} help={help}>
|
|
166
|
-
<div className="flex items-center gap-3 rounded-lg bg-muted/70 px-3 py-2.5">
|
|
167
|
-
<Slider
|
|
168
|
-
min={min}
|
|
169
|
-
max={max}
|
|
170
|
-
step={step}
|
|
171
|
-
value={shown}
|
|
172
|
-
disabled={disabled}
|
|
173
|
-
className="flex-1 [&_[data-slot=slider-track]]:bg-muted-foreground/25"
|
|
174
|
-
onValueChange={(next) => {
|
|
175
|
-
if (live) onChange(next as number);
|
|
176
|
-
else setDraft({ key: value, value: next as number });
|
|
177
|
-
}}
|
|
178
|
-
onValueCommitted={() => {
|
|
179
|
-
if (!live && shown !== value) onChange(shown);
|
|
180
|
-
}}
|
|
181
|
-
/>
|
|
182
|
-
<span className="w-9 shrink-0 text-end text-xs tabular-nums text-muted-foreground">
|
|
183
|
-
{format(shown)}
|
|
184
|
-
</span>
|
|
185
|
-
</div>
|
|
186
|
-
</ReadabilityField>
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function Segment<T extends string>({
|
|
191
|
-
options,
|
|
192
|
-
value,
|
|
193
|
-
onChange,
|
|
194
|
-
disabled,
|
|
195
|
-
itemContent,
|
|
196
|
-
itemLabel,
|
|
197
|
-
itemHelp,
|
|
198
|
-
itemClassName,
|
|
199
|
-
}: {
|
|
200
|
-
options: readonly T[];
|
|
201
|
-
value: T;
|
|
202
|
-
onChange: (value: T) => void;
|
|
203
|
-
disabled?: boolean;
|
|
204
|
-
itemContent: (id: T) => ReactNode;
|
|
205
|
-
itemLabel: (id: T) => string;
|
|
206
|
-
itemHelp: (id: T) => string;
|
|
207
|
-
itemClassName?: string;
|
|
208
|
-
}) {
|
|
209
|
-
return (
|
|
210
|
-
<ToggleGroup
|
|
211
|
-
value={[value]}
|
|
212
|
-
onValueChange={(values) => {
|
|
213
|
-
const next = values.at(-1) as T | undefined;
|
|
214
|
-
if (next && next !== value) onChange(next);
|
|
215
|
-
}}
|
|
216
|
-
disabled={disabled}
|
|
217
|
-
size="sm"
|
|
218
|
-
spacing={1}
|
|
219
|
-
className="w-full rounded-lg bg-muted/70 p-1"
|
|
220
|
-
>
|
|
221
|
-
{options.map((id) => (
|
|
222
|
-
<Hint key={id} label={itemHelp(id)}>
|
|
223
|
-
<ToggleGroupItem
|
|
224
|
-
value={id}
|
|
225
|
-
aria-label={itemLabel(id)}
|
|
226
|
-
className={cn(
|
|
227
|
-
'flex-1 rounded-md text-muted-foreground hover:text-foreground aria-pressed:bg-background aria-pressed:text-foreground aria-pressed:shadow-sm',
|
|
228
|
-
itemClassName,
|
|
229
|
-
)}
|
|
230
|
-
>
|
|
231
|
-
{itemContent(id)}
|
|
232
|
-
</ToggleGroupItem>
|
|
233
|
-
</Hint>
|
|
234
|
-
))}
|
|
235
|
-
</ToggleGroup>
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function WidthField({
|
|
240
|
-
field,
|
|
241
|
-
icon,
|
|
242
|
-
value,
|
|
243
|
-
onChange,
|
|
244
|
-
}: {
|
|
245
|
-
field: 'page_width' | 'content_width';
|
|
246
|
-
icon: ReactNode;
|
|
247
|
-
value: number;
|
|
248
|
-
onChange: (value: number) => void;
|
|
249
|
-
}) {
|
|
250
|
-
const t = useTranslations();
|
|
251
|
-
return (
|
|
252
|
-
<ReadabilitySlider
|
|
253
|
-
live
|
|
254
|
-
label={t(`ui.readability.${field}`)}
|
|
255
|
-
icon={icon}
|
|
256
|
-
help={
|
|
257
|
-
<ReadabilityHelp
|
|
258
|
-
title={t(`ui.readability.${field}`)}
|
|
259
|
-
description={t(`ui.readability.${field}.help`)}
|
|
260
|
-
items={[
|
|
261
|
-
{
|
|
262
|
-
icon,
|
|
263
|
-
label: t(`ui.readability.${field}.slider`),
|
|
264
|
-
help: t(`ui.readability.${field}.slider.help`),
|
|
265
|
-
},
|
|
266
|
-
]}
|
|
267
|
-
/>
|
|
268
|
-
}
|
|
269
|
-
value={value}
|
|
270
|
-
onChange={onChange}
|
|
271
|
-
min={60}
|
|
272
|
-
max={100}
|
|
273
|
-
step={1}
|
|
274
|
-
format={(v) => `${v}%`}
|
|
275
|
-
/>
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
54
|
export function ReadabilityMenu({
|
|
280
55
|
anchor,
|
|
281
56
|
}: {
|
|
@@ -297,6 +72,46 @@ export function ReadabilityMenu({
|
|
|
297
72
|
smallScreen,
|
|
298
73
|
} = useReadability();
|
|
299
74
|
|
|
75
|
+
const layoutT: Record<ReadabilityLayout, { label: string; help: string }> = {
|
|
76
|
+
full: {
|
|
77
|
+
label: t('Expand all', { note: 'ui.readability.layout.full' }),
|
|
78
|
+
help: t('The article takes the full panel width; the sidebar stays at the screen edge', { note: 'ui.readability.layout.full.help' }),
|
|
79
|
+
},
|
|
80
|
+
sidebar: {
|
|
81
|
+
label: t('Adjustable page width', { note: 'ui.readability.layout.sidebar' }),
|
|
82
|
+
help: t('The slider narrows the whole page block: the sidebar moves with it', { note: 'ui.readability.layout.sidebar.help' }),
|
|
83
|
+
},
|
|
84
|
+
both: {
|
|
85
|
+
label: t('Adjustable page and content', { note: 'ui.readability.layout.both' }),
|
|
86
|
+
help: t('Both sliders apply: page block width and the text column inside', { note: 'ui.readability.layout.both.help' }),
|
|
87
|
+
},
|
|
88
|
+
original: {
|
|
89
|
+
label: t('Original width', { note: 'ui.readability.layout.original' }),
|
|
90
|
+
help: t('Default centered page width', { note: 'ui.readability.layout.original.help' }),
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const spotlightT: Record<
|
|
94
|
+
SpotlightStyle | 'on' | 'off',
|
|
95
|
+
{ label: string; help: string }
|
|
96
|
+
> = {
|
|
97
|
+
on: {
|
|
98
|
+
label: t('On', { note: 'ui.readability.spotlight.on' }),
|
|
99
|
+
help: t('Turn on Spotlight', { note: 'ui.readability.spotlight.on.help' }),
|
|
100
|
+
},
|
|
101
|
+
off: {
|
|
102
|
+
label: t('Off', { note: 'ui.readability.spotlight.off' }),
|
|
103
|
+
help: t('Turn off Spotlight', { note: 'ui.readability.spotlight.off.help' }),
|
|
104
|
+
},
|
|
105
|
+
under: {
|
|
106
|
+
label: t('Under', { note: 'ui.readability.spotlight.under' }),
|
|
107
|
+
help: t('A solid background under the hovered block shows where the cursor is', { note: 'ui.readability.spotlight.under.help' }),
|
|
108
|
+
},
|
|
109
|
+
aside: {
|
|
110
|
+
label: t('Aside', { note: 'ui.readability.spotlight.aside' }),
|
|
111
|
+
help: t('A solid line beside the hovered element shows where the cursor is', { note: 'ui.readability.spotlight.aside.help' }),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
|
|
300
115
|
const layoutDisabled = smallScreen;
|
|
301
116
|
const pathname = usePathname();
|
|
302
117
|
const spotlightDisabled =
|
|
@@ -310,7 +125,7 @@ export function ReadabilityMenu({
|
|
|
310
125
|
variant="ghost"
|
|
311
126
|
size="icon-sm"
|
|
312
127
|
tooltip={false}
|
|
313
|
-
label={t('ui.readability.menu')}
|
|
128
|
+
label={t('Readability', { note: 'ui.readability.menu' })}
|
|
314
129
|
className="text-muted-foreground! data-[popup-open]:bg-accent! data-[popup-open]:text-accent-foreground!"
|
|
315
130
|
>
|
|
316
131
|
<Settings />
|
|
@@ -325,20 +140,20 @@ export function ReadabilityMenu({
|
|
|
325
140
|
className="w-64 space-y-3 p-3"
|
|
326
141
|
>
|
|
327
142
|
<ReadabilityField
|
|
328
|
-
label={t('ui.readability.layout')}
|
|
143
|
+
label={t('Layout', { note: 'ui.readability.layout' })}
|
|
329
144
|
icon={<Proportions />}
|
|
330
145
|
help={
|
|
331
146
|
<ReadabilityHelp
|
|
332
|
-
title={t('ui.readability.layout')}
|
|
147
|
+
title={t('Layout', { note: 'ui.readability.layout' })}
|
|
333
148
|
description={
|
|
334
149
|
layoutDisabled
|
|
335
|
-
? t('ui.readability.layout.unavailable')
|
|
336
|
-
: t('ui.readability.layout.help')
|
|
150
|
+
? t('Layout switch is not available on small screens', { note: 'ui.readability.layout.unavailable' })
|
|
151
|
+
: t('Adjust the site layout to fit different reading needs and screens', { note: 'ui.readability.layout.help' })
|
|
337
152
|
}
|
|
338
153
|
items={readabilityLayouts.map((id) => ({
|
|
339
154
|
icon: layoutIcons[id],
|
|
340
|
-
label:
|
|
341
|
-
help:
|
|
155
|
+
label: layoutT[id].label,
|
|
156
|
+
help: layoutT[id].help,
|
|
342
157
|
}))}
|
|
343
158
|
/>
|
|
344
159
|
}
|
|
@@ -349,8 +164,8 @@ export function ReadabilityMenu({
|
|
|
349
164
|
onChange={setLayout}
|
|
350
165
|
disabled={layoutDisabled}
|
|
351
166
|
itemContent={(id) => layoutIcons[id]}
|
|
352
|
-
itemLabel={(id) =>
|
|
353
|
-
itemHelp={(id) =>
|
|
167
|
+
itemLabel={(id) => layoutT[id].label}
|
|
168
|
+
itemHelp={(id) => layoutT[id].help}
|
|
354
169
|
/>
|
|
355
170
|
</ReadabilityField>
|
|
356
171
|
{(layout === 'sidebar' || layout === 'both') && !smallScreen && (
|
|
@@ -370,19 +185,19 @@ export function ReadabilityMenu({
|
|
|
370
185
|
/>
|
|
371
186
|
)}
|
|
372
187
|
<ReadabilityField
|
|
373
|
-
label={t('ui.readability.spotlight')}
|
|
188
|
+
label={t('Spotlight', { note: 'ui.readability.spotlight' })}
|
|
374
189
|
icon={<Flashlight />}
|
|
375
190
|
help={
|
|
376
191
|
<ReadabilityHelp
|
|
377
|
-
title={t('ui.readability.spotlight')}
|
|
192
|
+
title={t('Spotlight', { note: 'ui.readability.spotlight' })}
|
|
378
193
|
description={
|
|
379
194
|
spotlightDisabled
|
|
380
|
-
? t('ui.readability.spotlight.unavailable')
|
|
381
|
-
: t('ui.readability.spotlight.help')
|
|
195
|
+
? t('Spotlight is not available on touch screens', { note: 'ui.readability.spotlight.unavailable' })
|
|
196
|
+
: t('Highlight the line or block under the cursor to help readers stay focused', { note: 'ui.readability.spotlight.help' })
|
|
382
197
|
}
|
|
383
198
|
items={switches.map((id) => ({
|
|
384
|
-
label:
|
|
385
|
-
help:
|
|
199
|
+
label: spotlightT[id].label,
|
|
200
|
+
help: spotlightT[id].help,
|
|
386
201
|
}))}
|
|
387
202
|
/>
|
|
388
203
|
}
|
|
@@ -392,24 +207,24 @@ export function ReadabilityMenu({
|
|
|
392
207
|
value={spotlight ? 'on' : 'off'}
|
|
393
208
|
onChange={(id) => setSpotlight(id === 'on')}
|
|
394
209
|
disabled={spotlightDisabled}
|
|
395
|
-
itemContent={(id) =>
|
|
396
|
-
itemLabel={(id) =>
|
|
397
|
-
itemHelp={(id) =>
|
|
210
|
+
itemContent={(id) => spotlightT[id].label}
|
|
211
|
+
itemLabel={(id) => spotlightT[id].label}
|
|
212
|
+
itemHelp={(id) => spotlightT[id].help}
|
|
398
213
|
itemClassName="text-xs"
|
|
399
214
|
/>
|
|
400
215
|
</ReadabilityField>
|
|
401
216
|
{spotlight && !spotlightDisabled && (
|
|
402
217
|
<ReadabilityField
|
|
403
|
-
label={t('ui.readability.spotlight.styles')}
|
|
218
|
+
label={t('Style', { note: 'ui.readability.spotlight.styles' })}
|
|
404
219
|
icon={<Palette />}
|
|
405
220
|
help={
|
|
406
221
|
<ReadabilityHelp
|
|
407
|
-
title={t('ui.readability.spotlight.styles')}
|
|
408
|
-
description={t('ui.readability.spotlight.styles.help')}
|
|
222
|
+
title={t('Style', { note: 'ui.readability.spotlight.styles' })}
|
|
223
|
+
description={t('Adjust how the spotlight highlight is drawn', { note: 'ui.readability.spotlight.styles.help' })}
|
|
409
224
|
items={spotlightStyles.map((id) => ({
|
|
410
225
|
icon: styleIcons[id],
|
|
411
|
-
label:
|
|
412
|
-
help:
|
|
226
|
+
label: spotlightT[id].label,
|
|
227
|
+
help: spotlightT[id].help,
|
|
413
228
|
}))}
|
|
414
229
|
/>
|
|
415
230
|
}
|
|
@@ -420,8 +235,8 @@ export function ReadabilityMenu({
|
|
|
420
235
|
onChange={setSpotlightStyle}
|
|
421
236
|
disabled={spotlightDisabled || !spotlight}
|
|
422
237
|
itemContent={(id) => styleIcons[id]}
|
|
423
|
-
itemLabel={(id) =>
|
|
424
|
-
itemHelp={(id) =>
|
|
238
|
+
itemLabel={(id) => spotlightT[id].label}
|
|
239
|
+
itemHelp={(id) => spotlightT[id].help}
|
|
425
240
|
/>
|
|
426
241
|
</ReadabilityField>
|
|
427
242
|
)}
|
|
@@ -429,3 +244,4 @@ export function ReadabilityMenu({
|
|
|
429
244
|
</Popover>
|
|
430
245
|
);
|
|
431
246
|
}
|
|
247
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { usePathname } from 'next/navigation';
|
|
4
|
-
import { useReadability } from '@terrafirmagreg-team/ui/hooks/readability/
|
|
4
|
+
import { useReadability } from '@terrafirmagreg-team/ui/hooks/readability/use-readability';
|
|
5
5
|
import { readabilityConfig } from '@terrafirmagreg-team/ui/lib/readability/config';
|
|
6
6
|
import {
|
|
7
7
|
contentRoot,
|
|
@@ -162,3 +162,4 @@ export function SpotlightTrack() {
|
|
|
162
162
|
/>
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
|
+
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
SearchTagsListContext,
|
|
25
25
|
useSearch,
|
|
26
26
|
useSearchTagsList,
|
|
27
|
-
} from '@terrafirmagreg-team/ui/hooks/search/dialog';
|
|
27
|
+
} from '@terrafirmagreg-team/ui/hooks/search/use-dialog';
|
|
28
28
|
import type {
|
|
29
29
|
ReactSortedResult,
|
|
30
30
|
SearchItemType,
|
|
@@ -239,12 +239,12 @@ export function SearchDialogInput(props: ComponentProps<'input'>) {
|
|
|
239
239
|
role="combobox"
|
|
240
240
|
aria-expanded={active !== null}
|
|
241
241
|
aria-activedescendant={active !== null ? `search-option-${active}` : undefined}
|
|
242
|
-
aria-label={t('ui.search_dialog')}
|
|
242
|
+
aria-label={t('Search', { note: 'ui.search_dialog' })}
|
|
243
243
|
aria-autocomplete="list"
|
|
244
244
|
aria-controls="search-list"
|
|
245
245
|
value={search}
|
|
246
246
|
onChange={(e) => onSearchChange(e.target.value)}
|
|
247
|
-
placeholder={t('ui.search_dialog')}
|
|
247
|
+
placeholder={t('Search', { note: 'ui.search_dialog' })}
|
|
248
248
|
{...props}
|
|
249
249
|
className={cn(
|
|
250
250
|
'w-0 flex-1 bg-transparent text-lg placeholder:text-muted-foreground focus-visible:outline-none',
|
|
@@ -265,7 +265,7 @@ export function SearchDialogClose({
|
|
|
265
265
|
return (
|
|
266
266
|
<button
|
|
267
267
|
type="button"
|
|
268
|
-
aria-label={t('ui.search_close')}
|
|
268
|
+
aria-label={t('Close Search', { note: 'ui.search_close' })}
|
|
269
269
|
onClick={() => onOpenChange(false)}
|
|
270
270
|
className={cn(
|
|
271
271
|
buttonVariants({
|
|
@@ -337,7 +337,7 @@ export function SearchDialogContent({
|
|
|
337
337
|
}
|
|
338
338
|
{...props}
|
|
339
339
|
>
|
|
340
|
-
<Dialog.Title className="hidden">{t('ui.search_dialog')}</Dialog.Title>
|
|
340
|
+
<Dialog.Title className="hidden">{t('Search', { note: 'ui.search_dialog' })}</Dialog.Title>
|
|
341
341
|
{children}
|
|
342
342
|
</Dialog.Popup>
|
|
343
343
|
</Dialog.Portal>
|
|
@@ -427,7 +427,7 @@ export function SearchDialogList({
|
|
|
427
427
|
ref={ref}
|
|
428
428
|
data-empty={items === null && !error}
|
|
429
429
|
className={cn(
|
|
430
|
-
'overflow-hidden h-(--animated-height) transition-[height]
|
|
430
|
+
'overflow-hidden h-(--animated-height) transition-[height]',
|
|
431
431
|
props.className,
|
|
432
432
|
)}
|
|
433
433
|
>
|
|
@@ -455,8 +455,8 @@ export function SearchDialogList({
|
|
|
455
455
|
id="search-list"
|
|
456
456
|
|
|
457
457
|
role={items?.length ? 'listbox' : undefined}
|
|
458
|
-
aria-label={items?.length ? t('ui.search_dialog') : undefined}
|
|
459
|
-
className={cn('w-full flex flex-col overflow-y-auto max-h-[460px] p-1', !items && 'hidden')}
|
|
458
|
+
aria-label={items?.length ? t('Search', { note: 'ui.search_dialog' }) : undefined}
|
|
459
|
+
className={cn('w-full flex flex-col overflow-y-auto max-h-[460px] p-1 scrollbar-thin', !items && 'hidden')}
|
|
460
460
|
>
|
|
461
461
|
{items?.length === 0 && Empty()}
|
|
462
462
|
|
|
@@ -530,12 +530,14 @@ export function SearchDialogListItem({
|
|
|
530
530
|
className,
|
|
531
531
|
children,
|
|
532
532
|
renderMarkdown,
|
|
533
|
+
badge,
|
|
533
534
|
...props
|
|
534
535
|
}: ComponentProps<'div'> & {
|
|
535
536
|
|
|
536
537
|
table?: SearchResultTable;
|
|
537
538
|
|
|
538
539
|
renderMarkdown?: (v: string) => ReactNode;
|
|
540
|
+
badge?: ReactNode;
|
|
539
541
|
item: SearchItemType;
|
|
540
542
|
}) {
|
|
541
543
|
const { query, getActive, setActive, subscribeActive } = useSearch();
|
|
@@ -569,7 +571,17 @@ export function SearchDialogListItem({
|
|
|
569
571
|
} else {
|
|
570
572
|
children ??= (
|
|
571
573
|
<>
|
|
572
|
-
|
|
574
|
+
{badge != null && (
|
|
575
|
+
<span className="absolute end-2 top-1.5 rounded-md border border-border bg-background px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-foreground/80">
|
|
576
|
+
{badge}
|
|
577
|
+
</span>
|
|
578
|
+
)}
|
|
579
|
+
<div
|
|
580
|
+
className={cn(
|
|
581
|
+
'inline-flex items-center text-muted-foreground text-xs empty:hidden',
|
|
582
|
+
badge != null && 'pe-14',
|
|
583
|
+
)}
|
|
584
|
+
>
|
|
573
585
|
{item.breadcrumbs?.map((item, i) => (
|
|
574
586
|
<Fragment key={i}>
|
|
575
587
|
{i > 0 && <ChevronRight className="size-4 rtl:rotate-180" />}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
dialogHandle,
|
|
13
13
|
SearchContext,
|
|
14
14
|
useSearchContext,
|
|
15
|
-
} from '@terrafirmagreg-team/ui/hooks/search/provider';
|
|
15
|
+
} from '@terrafirmagreg-team/ui/hooks/search/use-provider';
|
|
16
16
|
import type { SharedProps } from '@terrafirmagreg-team/ui/types/search';
|
|
17
17
|
export type { SharedProps } from '@terrafirmagreg-team/ui/types/search';
|
|
18
18
|
|
|
@@ -115,3 +115,4 @@ export function SearchOnly({ children }: { children: ReactNode }) {
|
|
|
115
115
|
|
|
116
116
|
if (search.enabled) return children;
|
|
117
117
|
}
|
|
118
|
+
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
useAutoScroll,
|
|
23
23
|
useFolderDepth,
|
|
24
24
|
useSidebar,
|
|
25
|
-
} from '@terrafirmagreg-team/ui/hooks/sidebar';
|
|
25
|
+
} from '@terrafirmagreg-team/ui/hooks/use-sidebar';
|
|
26
26
|
import { Link } from '@terrafirmagreg-team/ui/components/blocks/nav/link';
|
|
27
27
|
import {
|
|
28
28
|
Collapsible,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
} from '@terrafirmagreg-team/ui/components/ui/collapsible';
|
|
32
32
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
33
33
|
|
|
34
|
-
export type { SidebarContextValue } from '@terrafirmagreg-team/ui/hooks/sidebar';
|
|
34
|
+
export type { SidebarContextValue } from '@terrafirmagreg-team/ui/hooks/use-sidebar';
|
|
35
35
|
|
|
36
36
|
export function SidebarProvider({
|
|
37
37
|
defaultOpenLevel = 0,
|
|
@@ -363,8 +363,8 @@ export function SidebarTrigger({
|
|
|
363
363
|
type="button"
|
|
364
364
|
aria-label={
|
|
365
365
|
open
|
|
366
|
-
? t('ui.sidebar_close'
|
|
367
|
-
: t('ui.sidebar_open'
|
|
366
|
+
? t('Close Sidebar', { note: 'ui.sidebar_close' })
|
|
367
|
+
: t('Open Sidebar', { note: 'ui.sidebar_open' })
|
|
368
368
|
}
|
|
369
369
|
aria-expanded={open}
|
|
370
370
|
aria-controls="sidebar-mobile"
|
|
@@ -383,7 +383,7 @@ export function SidebarCollapseTrigger(props: ComponentProps<'button'>) {
|
|
|
383
383
|
return (
|
|
384
384
|
<button
|
|
385
385
|
type="button"
|
|
386
|
-
aria-label={t('ui.sidebar_collapse'
|
|
386
|
+
aria-label={t('Collapse Sidebar', { note: 'ui.sidebar_collapse' })}
|
|
387
387
|
aria-controls="sidebar"
|
|
388
388
|
aria-expanded={!collapsed}
|
|
389
389
|
data-collapsed={collapsed}
|