@rigel-cms/rigel 1.4.0-rc.1 → 1.4.0-rc.2
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/docs/content-locales.md +11 -2
- package/package.json +1 -1
- package/src/config.ts +5 -0
- package/src/editor/App.svelte +38 -13
- package/src/editor/i18n/de/shell.json +6 -1
- package/src/editor/i18n/en/shell.json +6 -1
- package/src/editor/i18n/es/shell.json +6 -1
- package/src/editor/i18n/fr/shell.json +6 -1
- package/src/editor/i18n/it/shell.json +6 -1
- package/src/editor/i18n/ja/shell.json +6 -1
- package/src/editor/i18n/nl/shell.json +6 -1
- package/src/editor/i18n/ru/shell.json +6 -1
- package/src/editor/i18n/zh/shell.json +6 -1
- package/src/editor/lib/Settings.svelte +37 -30
- package/src/editor/lib/TopBar.svelte +13 -49
- package/src/editor/lib/settings.ts +2 -0
- package/src/editor/styles.css +10 -0
- package/src/index.ts +1 -1
- package/src/manifest/pages.ts +0 -4
- package/src/manifest/search.ts +41 -0
- package/src/manifest/types.ts +1 -0
- package/src/vite/manifest-plugin.ts +2 -1
package/docs/content-locales.md
CHANGED
|
@@ -4,6 +4,7 @@ Content languages are optional. Configure them separately from `languages`, whic
|
|
|
4
4
|
|
|
5
5
|
```js
|
|
6
6
|
rigel({
|
|
7
|
+
search: { scope: "all" },
|
|
7
8
|
contentLocales: {
|
|
8
9
|
defaultLocale: "en",
|
|
9
10
|
locales: [
|
|
@@ -44,7 +45,15 @@ defineDocument({
|
|
|
44
45
|
|
|
45
46
|
Each type can have different fields, allowed sections and initial content. You can also annotate an existing `defineCopy` page with `locale`. Rigel reads the annotation, not the folder name.
|
|
46
47
|
|
|
47
|
-
With multiple configured locales, the editor shows a Content language dropdown. It filters navigation, page lists
|
|
48
|
+
With multiple configured locales, the editor shows a Content language dropdown. It filters navigation, page lists and collections. Switching away from unsaved changes offers the existing Save, Discard and Keep editing choices. New sessions start with `defaultLocale`.
|
|
49
|
+
|
|
50
|
+
## Global search
|
|
51
|
+
|
|
52
|
+
Sidebar search (⌘/Ctrl + K) searches **all content languages** by default. Set `search: { scope: "currentLocale" }` in your Astro integration to default to the selected content language plus shared resources instead. Missing or invalid scope values fall back to `"all"`. This setting does not change the Pages table's language filter or its live search.
|
|
53
|
+
|
|
54
|
+
In **Settings → Content → Search content languages**, editors can choose **Website default**, **All languages**, or **Current content language**, alongside the article editing preferences. The override applies immediately and persists in `rigelcms.settings.userPref.searchScope` in this browser. Website default removes the override; Erase All Data resets it with the other browser preferences. It is independent of the editor interface language, which lives in **Settings → Interface** after Theme and Accessibility. Current content language is disabled when there are fewer than two configured content languages.
|
|
55
|
+
|
|
56
|
+
Results include legacy pages, native documents, collections, posts and menus. Shared resources appear once. Localized results show two-character language codes when multiple content languages exist, with the full configured label available on hover and to screen readers. Opening another language's result switches content language and opens that exact resource through the existing unsaved-changes guard; Keep editing leaves the language and draft untouched. Search still shows at most 12 results.
|
|
48
57
|
|
|
49
58
|
Content without a `locale` remains visible in every language. Use this for common authors, media and other shared records. A shared document type can also have a locale when its records are language-specific. The language filter is an editing convenience, not an access-control boundary: the manifest and publishing batch still contain the site's complete content.
|
|
50
59
|
|
|
@@ -54,7 +63,7 @@ Your Astro routes and templates still own website localization, including public
|
|
|
54
63
|
|
|
55
64
|
## Page categories and templates
|
|
56
65
|
|
|
57
|
-
Set `category` on a nonshared document definition to group its pages, for example `category: "Articles"`. This is schema metadata, not an editable field in stored documents. Uncategorised native documents and legacy copy pages remain in **Core** without a migration. When categories exist, the
|
|
66
|
+
Set `category` on a nonshared document definition to group its pages, for example `category: "Articles"`. This is schema metadata, not an editable field in stored documents. Uncategorised native documents and legacy copy pages remain in **Core** without a migration. Pages initially shows **All pages**. When categories exist, the table offers Core, All pages and each category visible in the selected content language. The live search matches title/name, slug and category substrings, ignores case and surrounding whitespace, and combines with the selected category. Its clear button restores input focus. Back preserves both the query and originating table/filter, not a filter inferred from the opened page. Content-language switches retain the query and reset unavailable categories to All pages.
|
|
58
67
|
|
|
59
68
|
Add new asks for a page name and a category from the locale-eligible, creatable document types. When a category contains multiple types, the editor also requires a Template choice using each definition's title. Shared, singleton and `creatable: false` types are not creation choices. A single uncategorised type keeps the name-only dialog. Creation uses the selected type's identity, initial values and section template; nothing is written until Save.
|
|
60
69
|
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -9,12 +9,15 @@ import { DEFAULT_MAX_FILE_SIZE } from "./media-types";
|
|
|
9
9
|
import { systemMessage } from "./editor/i18n/format";
|
|
10
10
|
import { localeNames, type Locale } from "./editor/i18n/locales";
|
|
11
11
|
import { validateContentLocales, validateContentLocale } from "./manifest/content-locales";
|
|
12
|
+
import { effectiveSearchScope } from "./manifest/search";
|
|
12
13
|
|
|
13
14
|
export interface ContentLocalesConfig {
|
|
14
15
|
defaultLocale: string;
|
|
15
16
|
locales: { code: string; label: string }[];
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
export type SearchScope = "all" | "currentLocale";
|
|
20
|
+
|
|
18
21
|
export interface RigelConfig {
|
|
19
22
|
copyDir: string;
|
|
20
23
|
mediaDir?: string;
|
|
@@ -25,6 +28,7 @@ export interface RigelConfig {
|
|
|
25
28
|
demo?: boolean;
|
|
26
29
|
languages?: Locale[];
|
|
27
30
|
contentLocales?: ContentLocalesConfig;
|
|
31
|
+
search?: { scope: SearchScope };
|
|
28
32
|
extensions?: string[];
|
|
29
33
|
collections?: CollectionConfig[];
|
|
30
34
|
menus?: { folder: string; maxDepth?: number };
|
|
@@ -56,6 +60,7 @@ const DEFAULTS: Pick<RigelConfig, "copyDir" | "backend"> = {
|
|
|
56
60
|
|
|
57
61
|
export function parseConfig(options: Partial<RigelConfig> = {}): RigelConfig {
|
|
58
62
|
const cfg = { ...DEFAULTS, ...options };
|
|
63
|
+
cfg.search = { scope: effectiveSearchScope(cfg.search, undefined) };
|
|
59
64
|
if (cfg.contentLocales !== undefined) {
|
|
60
65
|
validateContentLocales(cfg.contentLocales);
|
|
61
66
|
cfg.contentLocales = { ...cfg.contentLocales, locales: cfg.contentLocales.locales.map(locale => ({ ...locale })) };
|
package/src/editor/App.svelte
CHANGED
|
@@ -12,10 +12,11 @@
|
|
|
12
12
|
import SitePreview from "./lib/SitePreview.svelte";
|
|
13
13
|
import ImagePicker from "./lib/ImagePicker.svelte";
|
|
14
14
|
import Settings from "./lib/Settings.svelte";
|
|
15
|
+
import { effectiveSearchScope } from "../manifest/search";
|
|
15
16
|
import TopBar from "./lib/TopBar.svelte";
|
|
16
17
|
import Select from "./lib/Select.svelte";
|
|
17
18
|
import { contentForLocale, selectionInContent } from "../manifest/content-locales";
|
|
18
|
-
import { pageCategories, pageTemplates, inPageCategory
|
|
19
|
+
import { pageCategories, pageTemplates, inPageCategory } from "../manifest/pages";
|
|
19
20
|
import { validateDocumentSlugs } from "../slugs";
|
|
20
21
|
import Shortcuts from "./lib/Shortcuts.svelte";
|
|
21
22
|
import CollectionPanel from "./lib/CollectionPanel.svelte";
|
|
@@ -160,7 +161,7 @@
|
|
|
160
161
|
if (!Array.isArray(data.pages) || (data.backend === "github" && !data.github)) throw new Error($t("shell.configurationIncomplete"));
|
|
161
162
|
blueprint = data;
|
|
162
163
|
manifest = data;
|
|
163
|
-
pagesFilter =
|
|
164
|
+
pagesFilter = null;
|
|
164
165
|
ready = true;
|
|
165
166
|
if (data.backend === "github" && data.github) {
|
|
166
167
|
const session = readSession(data.github);
|
|
@@ -480,7 +481,9 @@
|
|
|
480
481
|
});
|
|
481
482
|
|
|
482
483
|
const sidebar = $derived(buildSidebar(visibleContent, $t));
|
|
483
|
-
let pagesFilter = $state<string | null>(
|
|
484
|
+
let pagesFilter = $state<string | null>(null);
|
|
485
|
+
let pagesQuery = $state("");
|
|
486
|
+
let pagesSearch: HTMLInputElement;
|
|
484
487
|
let entryList = $state<Sel | null>(null);
|
|
485
488
|
const categories = $derived(pageCategories(visibleContent));
|
|
486
489
|
const pageCategory = $derived(sel?.kind === "pages" && sel.category !== undefined ? sel.category : pagesFilter);
|
|
@@ -521,11 +524,12 @@
|
|
|
521
524
|
...visibleContent.pages.filter((p) => !p.shared && inPageCategory(p.category, pageCategory))
|
|
522
525
|
.map((p) => ({ slug: p.slug, path: previewPath(manifest.preview ?? {}, { kind: "page", slug: p.slug }), title: p.label, category: p.category, draft: false, target: { kind: "page", slug: p.slug } as Sel })),
|
|
523
526
|
...pageDocuments
|
|
524
|
-
.map((doc) => ({ slug: effectiveSlug(doc.data, doc.slug), path: previewPath(manifest.preview ?? {}, { kind: "document", type: doc.type, slug: effectiveSlug(doc.data, doc.slug) }), title: String(doc.data.title ?? doc.slug), category: docTypes.find(type => type.name === doc.type)?.category, draft: doc.data.draft === true, target: { kind: "document", type: doc.type, id: doc.id } as Sel }))
|
|
527
|
+
.map((doc) => ({ slug: effectiveSlug(doc.data, doc.slug), path: previewPath(manifest.preview ?? {}, { kind: "document", type: doc.type, slug: effectiveSlug(doc.data, doc.slug) }), title: String(doc.data.title ?? doc.data.name ?? doc.slug), category: docTypes.find(type => type.name === doc.type)?.category, draft: doc.data.draft === true, target: { kind: "document", type: doc.type, id: doc.id } as Sel }))
|
|
525
528
|
.filter(doc => inPageCategory(doc.category, pageCategory)),
|
|
526
529
|
];
|
|
527
530
|
const key = sortBy === "slug" || sortBy === "category" || sortBy === "path" ? sortBy : "title";
|
|
528
|
-
|
|
531
|
+
const query = pagesQuery.trim().toLocaleLowerCase();
|
|
532
|
+
return rows.filter(row => !query || [row.title, row.slug, row.category ?? (categories.length ? $t("shell.coreCategory") : "")].some(value => value.toLocaleLowerCase().includes(query))).sort((a, b) => String(a[key] ?? "").localeCompare(String(b[key] ?? "")) * sortDir);
|
|
529
533
|
});
|
|
530
534
|
function documentCell(doc: ContentDocument, key: string): string {
|
|
531
535
|
return String(key === "title" ? doc.data.title ?? doc.data.name ?? doc.slug : key === "slug" ? activeDocType?.shared ? doc.slug : effectiveSlug(doc.data, doc.slug) : doc.data[key] ?? "");
|
|
@@ -694,19 +698,25 @@
|
|
|
694
698
|
void tick().then(() => document.querySelector<HTMLButtonElement>(".content-language button[role=combobox]")?.focus({ preventScroll: true }));
|
|
695
699
|
}
|
|
696
700
|
|
|
697
|
-
function switchContentLocale(value: string | string[]) {
|
|
701
|
+
function switchContentLocale(value: string | string[], target?: Sel) {
|
|
698
702
|
localeControlRevision += 1;
|
|
699
703
|
if (typeof value !== "string" || value === contentLocale || !manifest.contentLocales?.locales.some(locale => locale.code === value)) return;
|
|
700
704
|
requestNavigation(() => {
|
|
701
705
|
const destination = contentForLocale(manifest, value);
|
|
702
706
|
selectedContentLocale = value;
|
|
703
|
-
if (pagesFilter && !pageCategories(destination).includes(pagesFilter)) pagesFilter =
|
|
707
|
+
if (pagesFilter && !pageCategories(destination).includes(pagesFilter)) pagesFilter = null;
|
|
704
708
|
if (!selectionInContent(destination, entryList)) entryList = null;
|
|
705
709
|
if (!selectionInContent(destination, sel)) openSelection(null);
|
|
710
|
+
if (target && selectionInContent(destination, target)) openSelection(target);
|
|
706
711
|
focusContentLocale();
|
|
707
712
|
}, focusContentLocale);
|
|
708
713
|
}
|
|
709
714
|
|
|
715
|
+
function selectSearch(next: Sel, locale?: string) {
|
|
716
|
+
if (locale && locale !== contentLocale && manifest.contentLocales?.locales.some(entry => entry.code === locale)) switchContentLocale(locale, next);
|
|
717
|
+
else select(next);
|
|
718
|
+
}
|
|
719
|
+
|
|
710
720
|
function sort(key: string) {
|
|
711
721
|
if (sortBy === key) sortDir = sortDir === 1 ? -1 : 1;
|
|
712
722
|
else {
|
|
@@ -877,7 +887,7 @@
|
|
|
877
887
|
<img src={logo} alt="" width="32" height="32" />
|
|
878
888
|
<span>Rigel <span class="brand-suffix">CMS</span></span>
|
|
879
889
|
</a>
|
|
880
|
-
<TopBar manifest
|
|
890
|
+
<TopBar {manifest} {sel} {contentLocale} scope={effectiveSearchScope(manifest.search, settings.userPref.searchScope)} onselect={selectSearch} />
|
|
881
891
|
{#if manifest.contentLocales && manifest.contentLocales.locales.length > 1}
|
|
882
892
|
<div class="content-language" data-locale={contentLocale}>
|
|
883
893
|
{#key localeControlRevision}
|
|
@@ -995,10 +1005,24 @@
|
|
|
995
1005
|
{/if}
|
|
996
1006
|
<div class="titlebar">
|
|
997
1007
|
<h2>{sel.kind === "page" ? page?.label : sel.kind === "menu" ? String(draft.title || menu?.slug || "") : sel.kind === "post" ? String(draft.title || post?.slug || "") : sel.kind === "document" ? activeDocType?.singleton ? activeDocType.title : String(draft.title || draft.name || activeDocument?.slug || sel.id) : listTitle}</h2>
|
|
998
|
-
{#if sel.kind === "pages"
|
|
999
|
-
|
|
1008
|
+
{#if sel.kind === "pages"}
|
|
1009
|
+
{#if templates.length}
|
|
1010
|
+
<button class="primary" onclick={() => newPage(pageDocType, true)}>{$t("shell.addNew")}</button>
|
|
1011
|
+
{/if}
|
|
1012
|
+
<div class="pages-controls">
|
|
1013
|
+
{#if categories.length}
|
|
1014
|
+
<Select ariaLabel={$t("shell.category")} value={JSON.stringify(pageCategory)} options={[{ value: "null", label: $t("shell.allPages") }, { value: JSON.stringify(""), label: $t("shell.coreCategory") }, ...categories.map(category => ({ value: JSON.stringify(category), label: category }))]} onchange={(value) => { if (typeof value === "string") { pagesFilter = JSON.parse(value); select({ kind: "pages" }); } }} />
|
|
1015
|
+
{/if}
|
|
1016
|
+
<div class="pages-search">
|
|
1017
|
+
<Icon name="magnifier" size={18} />
|
|
1018
|
+
<input type="search" aria-label={$t("shell.searchPages")} placeholder={$t("shell.searchPages")} bind:value={pagesQuery} bind:this={pagesSearch} />
|
|
1019
|
+
{#if pagesQuery}
|
|
1020
|
+
<button type="button" class="icon-btn" aria-label={$t("fields.clear")} onclick={() => { pagesQuery = ""; pagesSearch.focus(); }}><Icon name="x" size={16} /></button>
|
|
1021
|
+
{/if}
|
|
1022
|
+
</div>
|
|
1023
|
+
</div>
|
|
1000
1024
|
{/if}
|
|
1001
|
-
{#if (sel?.kind === "collection" && coll) || (sel?.kind === "
|
|
1025
|
+
{#if (sel?.kind === "collection" && coll) || (sel?.kind === "documents" && activeDocType && activeDocType.creatable !== false && !activeDocType.singleton)}
|
|
1002
1026
|
<button class="primary" onclick={() => (sel?.kind === "collection" ? newPost(coll!) : newPage(sel?.kind === "documents" ? activeDocType : pageDocType, sel?.kind === "pages"))}>{$t("shell.addNew")}</button>
|
|
1003
1027
|
{/if}
|
|
1004
1028
|
{#if sel?.kind === "post" && post}
|
|
@@ -1025,6 +1049,7 @@
|
|
|
1025
1049
|
{:else if sel?.kind === "media"}
|
|
1026
1050
|
<ImagePicker inline />
|
|
1027
1051
|
{:else if sel?.kind === "pages" || sel?.kind === "documents" || sel?.kind === "menus" || (sel?.kind === "collection" && coll)}
|
|
1052
|
+
|
|
1028
1053
|
<div class="table-card">
|
|
1029
1054
|
<table>
|
|
1030
1055
|
<thead>
|
|
@@ -1061,7 +1086,7 @@
|
|
|
1061
1086
|
{/each}
|
|
1062
1087
|
</tr>
|
|
1063
1088
|
{:else}
|
|
1064
|
-
<tr><td colspan={columns.length}>{$t("shell.noEntries")}</td></tr>
|
|
1089
|
+
<tr><td colspan={columns.length}>{$t(sel?.kind === "pages" && pagesQuery.trim() ? "shell.noResults" : "shell.noEntries")}</td></tr>
|
|
1065
1090
|
{/each}
|
|
1066
1091
|
</tbody>
|
|
1067
1092
|
</table>
|
|
@@ -1166,7 +1191,7 @@
|
|
|
1166
1191
|
{/if}
|
|
1167
1192
|
|
|
1168
1193
|
{#if settingsOpen}
|
|
1169
|
-
<Settings bind:s={settings} github={githubMode ? manifest.github : undefined} user={githubAccess?.user} error={settingsError} saving={saveState === "saving"} onerase={eraseBrowserData} onclose={() => (settingsOpen = false)} />
|
|
1194
|
+
<Settings bind:s={settings} search={manifest.search} contentLocales={manifest.contentLocales} github={githubMode ? manifest.github : undefined} user={githubAccess?.user} error={settingsError} saving={saveState === "saving"} onerase={eraseBrowserData} onclose={() => (settingsOpen = false)} />
|
|
1170
1195
|
{/if}
|
|
1171
1196
|
{#if shortcutsOpen}
|
|
1172
1197
|
<Shortcuts onclose={() => (shortcutsOpen = false)} />
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Inhaltssprachen durchsuchen",
|
|
3
|
+
"shell.searchScopeDefault": "Website-Standard ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Alle Sprachen",
|
|
5
|
+
"shell.searchScopeCurrent": "Aktuelle Inhaltssprache",
|
|
2
6
|
"shell.coreCategory": "Standard",
|
|
7
|
+
"shell.searchPages": "Seiten suchen",
|
|
3
8
|
"shell.template": "Vorlage",
|
|
4
9
|
"shell.chooseTemplate": "Vorlage auswählen",
|
|
5
10
|
"shell.search": "Suchen",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "navigieren",
|
|
25
30
|
"shell.searchOpen": "öffnen",
|
|
26
31
|
"shell.searchClose": "schließen",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Benutzeroberfläche",
|
|
28
33
|
"shell.editor": "Editor",
|
|
29
34
|
"shell.connection": "Verbindung",
|
|
30
35
|
"shell.media": "Medien",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Search content languages",
|
|
3
|
+
"shell.searchScopeDefault": "Website default ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "All languages",
|
|
5
|
+
"shell.searchScopeCurrent": "Current content language",
|
|
2
6
|
"shell.coreCategory": "Core",
|
|
7
|
+
"shell.searchPages": "Search pages",
|
|
3
8
|
"shell.template": "Template",
|
|
4
9
|
"shell.chooseTemplate": "Choose a template",
|
|
5
10
|
"shell.search": "Search",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "navigate",
|
|
25
30
|
"shell.searchOpen": "open",
|
|
26
31
|
"shell.searchClose": "close",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Interface",
|
|
28
33
|
"shell.editor": "Editor",
|
|
29
34
|
"shell.connection": "Connection",
|
|
30
35
|
"shell.media": "Media",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Idiomas del contenido de búsqueda",
|
|
3
|
+
"shell.searchScopeDefault": "Predeterminado del sitio ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Todos los idiomas",
|
|
5
|
+
"shell.searchScopeCurrent": "Idioma actual del contenido",
|
|
2
6
|
"shell.coreCategory": "Principales",
|
|
7
|
+
"shell.searchPages": "Buscar páginas",
|
|
3
8
|
"shell.template": "Plantilla",
|
|
4
9
|
"shell.chooseTemplate": "Elige una plantilla",
|
|
5
10
|
"shell.search": "Buscar",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "navegar",
|
|
25
30
|
"shell.searchOpen": "abrir",
|
|
26
31
|
"shell.searchClose": "cerrar",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Interfaz",
|
|
28
33
|
"shell.editor": "Editor",
|
|
29
34
|
"shell.connection": "Conexión",
|
|
30
35
|
"shell.media": "Archivos multimedia",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Langues du contenu recherché",
|
|
3
|
+
"shell.searchScopeDefault": "Par défaut du site ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Toutes les langues",
|
|
5
|
+
"shell.searchScopeCurrent": "Langue actuelle du contenu",
|
|
2
6
|
"shell.coreCategory": "Principales",
|
|
7
|
+
"shell.searchPages": "Rechercher des pages",
|
|
3
8
|
"shell.template": "Modèle",
|
|
4
9
|
"shell.chooseTemplate": "Choisir un modèle",
|
|
5
10
|
"shell.search": "Rechercher",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "parcourir",
|
|
25
30
|
"shell.searchOpen": "ouvrir",
|
|
26
31
|
"shell.searchClose": "fermer",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Interface",
|
|
28
33
|
"shell.editor": "Éditeur",
|
|
29
34
|
"shell.connection": "Connexion",
|
|
30
35
|
"shell.media": "Médias",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Lingue dei contenuti da cercare",
|
|
3
|
+
"shell.searchScopeDefault": "Predefinito del sito ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Tutte le lingue",
|
|
5
|
+
"shell.searchScopeCurrent": "Lingua attuale del contenuto",
|
|
2
6
|
"shell.coreCategory": "Principali",
|
|
7
|
+
"shell.searchPages": "Cerca pagine",
|
|
3
8
|
"shell.template": "Modello",
|
|
4
9
|
"shell.chooseTemplate": "Scegli un modello",
|
|
5
10
|
"shell.search": "Cerca",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "naviga",
|
|
25
30
|
"shell.searchOpen": "apri",
|
|
26
31
|
"shell.searchClose": "chiudi",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Interfaccia",
|
|
28
33
|
"shell.editor": "Editor",
|
|
29
34
|
"shell.connection": "Connessione",
|
|
30
35
|
"shell.media": "Media",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "検索するコンテンツの言語",
|
|
3
|
+
"shell.searchScopeDefault": "サイトの既定値({scope})",
|
|
4
|
+
"shell.searchScopeAll": "すべての言語",
|
|
5
|
+
"shell.searchScopeCurrent": "現在のコンテンツ言語",
|
|
2
6
|
"shell.coreCategory": "基本",
|
|
7
|
+
"shell.searchPages": "ページを検索",
|
|
3
8
|
"shell.template": "テンプレート",
|
|
4
9
|
"shell.chooseTemplate": "テンプレートを選択",
|
|
5
10
|
"shell.search": "検索",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "移動",
|
|
25
30
|
"shell.searchOpen": "開く",
|
|
26
31
|
"shell.searchClose": "閉じる",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "インターフェース",
|
|
28
33
|
"shell.editor": "エディター",
|
|
29
34
|
"shell.connection": "接続",
|
|
30
35
|
"shell.media": "メディア",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Inhoudstalen doorzoeken",
|
|
3
|
+
"shell.searchScopeDefault": "Websitestandaard ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Alle talen",
|
|
5
|
+
"shell.searchScopeCurrent": "Huidige inhoudstaal",
|
|
2
6
|
"shell.coreCategory": "Standaard",
|
|
7
|
+
"shell.searchPages": "Pagina’s zoeken",
|
|
3
8
|
"shell.template": "Sjabloon",
|
|
4
9
|
"shell.chooseTemplate": "Kies een sjabloon",
|
|
5
10
|
"shell.search": "Zoeken",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "navigeren",
|
|
25
30
|
"shell.searchOpen": "openen",
|
|
26
31
|
"shell.searchClose": "sluiten",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Interface",
|
|
28
33
|
"shell.editor": "Editor",
|
|
29
34
|
"shell.connection": "Verbinding",
|
|
30
35
|
"shell.media": "Media",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "Языки содержимого для поиска",
|
|
3
|
+
"shell.searchScopeDefault": "По умолчанию на сайте ({scope})",
|
|
4
|
+
"shell.searchScopeAll": "Все языки",
|
|
5
|
+
"shell.searchScopeCurrent": "Текущий язык содержимого",
|
|
2
6
|
"shell.coreCategory": "Основные",
|
|
7
|
+
"shell.searchPages": "Поиск страниц",
|
|
3
8
|
"shell.template": "Шаблон",
|
|
4
9
|
"shell.chooseTemplate": "Выберите шаблон",
|
|
5
10
|
"shell.search": "Поиск",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "перемещение",
|
|
25
30
|
"shell.searchOpen": "открыть",
|
|
26
31
|
"shell.searchClose": "закрыть",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "Интерфейс",
|
|
28
33
|
"shell.editor": "Редактор",
|
|
29
34
|
"shell.connection": "Подключение",
|
|
30
35
|
"shell.media": "Медиафайлы",
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
+
"shell.searchScope": "搜索内容语言",
|
|
3
|
+
"shell.searchScopeDefault": "网站默认({scope})",
|
|
4
|
+
"shell.searchScopeAll": "所有语言",
|
|
5
|
+
"shell.searchScopeCurrent": "当前内容语言",
|
|
2
6
|
"shell.coreCategory": "基础",
|
|
7
|
+
"shell.searchPages": "搜索页面",
|
|
3
8
|
"shell.template": "模板",
|
|
4
9
|
"shell.chooseTemplate": "选择模板",
|
|
5
10
|
"shell.search": "搜索",
|
|
@@ -24,7 +29,7 @@
|
|
|
24
29
|
"shell.searchNavigate": "切换",
|
|
25
30
|
"shell.searchOpen": "打开",
|
|
26
31
|
"shell.searchClose": "关闭",
|
|
27
|
-
"shell.
|
|
32
|
+
"shell.interface": "界面",
|
|
28
33
|
"shell.editor": "编辑器",
|
|
29
34
|
"shell.connection": "连接",
|
|
30
35
|
"shell.media": "媒体",
|
|
@@ -5,9 +5,13 @@
|
|
|
5
5
|
import type { Manifest } from "../../manifest/types";
|
|
6
6
|
import type { GitHubAccess } from "./github-auth";
|
|
7
7
|
import Icon from "./Icon.svelte";
|
|
8
|
+
import Select from "./Select.svelte";
|
|
9
|
+
import { effectiveSearchScope } from "../../manifest/search";
|
|
8
10
|
|
|
9
|
-
let { s = $bindable(), github, user, error, saving, onerase, onclose }: {
|
|
11
|
+
let { s = $bindable(), search, contentLocales, github, user, error, saving, onerase, onclose }: {
|
|
10
12
|
s: RigelSettings;
|
|
13
|
+
search?: Manifest["search"];
|
|
14
|
+
contentLocales?: Manifest["contentLocales"];
|
|
11
15
|
github?: Manifest["github"];
|
|
12
16
|
user?: GitHubAccess["user"];
|
|
13
17
|
error: string;
|
|
@@ -16,8 +20,8 @@
|
|
|
16
20
|
onclose: () => void;
|
|
17
21
|
} = $props();
|
|
18
22
|
|
|
19
|
-
type PaneId = "
|
|
20
|
-
let active: PaneId = $state("
|
|
23
|
+
type PaneId = "interface" | "content" | "connection" | "media" | "advanced";
|
|
24
|
+
let active: PaneId = $state("interface");
|
|
21
25
|
|
|
22
26
|
const providerIds = Object.keys(STOCK_PROVIDERS) as (keyof typeof STOCK_PROVIDERS)[];
|
|
23
27
|
let keys = $state(getKeys());
|
|
@@ -26,20 +30,20 @@
|
|
|
26
30
|
let eraseError = $state("");
|
|
27
31
|
|
|
28
32
|
const panes: { id: PaneId; label: string }[] = $derived([
|
|
29
|
-
{ id: "
|
|
30
|
-
{ id: "
|
|
33
|
+
{ id: "interface", label: $t("shell.interface") },
|
|
34
|
+
{ id: "content", label: $t("shell.content") },
|
|
31
35
|
{ id: "connection", label: $t("shell.connection") },
|
|
32
36
|
{ id: "media", label: $t("shell.media") },
|
|
33
|
-
|
|
37
|
+
|
|
34
38
|
{ id: "advanced", label: $t("shell.advanced") },
|
|
35
39
|
]);
|
|
36
40
|
|
|
37
41
|
const PANE_ICONS = {
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
interface: "palette",
|
|
43
|
+
content: "fileText",
|
|
40
44
|
connection: "user",
|
|
41
45
|
media: "image",
|
|
42
|
-
|
|
46
|
+
|
|
43
47
|
advanced: "wrench",
|
|
44
48
|
} as const;
|
|
45
49
|
|
|
@@ -71,6 +75,7 @@
|
|
|
71
75
|
{#each panes as p (p.id)}
|
|
72
76
|
<button
|
|
73
77
|
class:active={active === p.id}
|
|
78
|
+
aria-current={active === p.id ? "true" : undefined}
|
|
74
79
|
onclick={() => (active = p.id)}
|
|
75
80
|
>
|
|
76
81
|
<span class="pico"><Icon name={PANE_ICONS[p.id]} size={24} /></span>
|
|
@@ -79,9 +84,9 @@
|
|
|
79
84
|
{/each}
|
|
80
85
|
</nav>
|
|
81
86
|
|
|
82
|
-
<div class="settings-pane">
|
|
83
|
-
{#if active === "
|
|
84
|
-
|
|
87
|
+
<div class="settings-pane" role="region" aria-label={panes.find(p => p.id === active)?.label}>
|
|
88
|
+
{#if active === "interface"}
|
|
89
|
+
|
|
85
90
|
<fieldset>
|
|
86
91
|
<legend>{$t("shell.theme")}</legend>
|
|
87
92
|
<div class="theme-options">
|
|
@@ -101,17 +106,30 @@
|
|
|
101
106
|
</label>
|
|
102
107
|
<p class="hint" id="high-contrast-hint">{$t("shell.highContrastHint")}</p>
|
|
103
108
|
</fieldset>
|
|
104
|
-
|
|
105
|
-
{:else if active === "editor"}
|
|
106
|
-
<h3>{$t("shell.editorExperience")}</h3>
|
|
107
109
|
<fieldset>
|
|
108
|
-
<legend>{$t("shell.
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
<legend>{$t("shell.editorInterfaceLanguage")}</legend>
|
|
111
|
+
{#each availableLocales as lang}
|
|
112
|
+
<label class="radio">
|
|
113
|
+
<input type="radio" bind:group={s.language} value={lang} />
|
|
114
|
+
<span>{localeNames[lang]}</span>
|
|
115
|
+
</label>
|
|
116
|
+
{/each}
|
|
111
117
|
</fieldset>
|
|
118
|
+
|
|
119
|
+
{:else if active === "content"}
|
|
120
|
+
|
|
121
|
+
<Select label={$t("shell.articleBodyEditing")} value={s.userPref.editorExperience ?? "inline"} options={[
|
|
122
|
+
{ value: "sidebar", label: $t("shell.sidebar") },
|
|
123
|
+
{ value: "inline", label: $t("shell.inlinePreview") },
|
|
124
|
+
]} onchange={value => s.userPref.editorExperience = value === "sidebar" ? "sidebar" : "inline"} />
|
|
112
125
|
<p class="hint">{$t("shell.editorExperienceHint")}</p>
|
|
126
|
+
<Select label={$t("shell.searchScope")} value={s.userPref.searchScope ?? "inherit"} options={[
|
|
127
|
+
{ value: "inherit", label: $t("shell.searchScopeDefault", { scope: $t(effectiveSearchScope(search, undefined) === "all" ? "shell.searchScopeAll" : "shell.searchScopeCurrent") }) },
|
|
128
|
+
{ value: "all", label: $t("shell.searchScopeAll") },
|
|
129
|
+
{ value: "currentLocale", label: $t("shell.searchScopeCurrent"), disabled: !contentLocales || contentLocales.locales.length < 2 },
|
|
130
|
+
]} onchange={value => s.userPref.searchScope = value === "all" || value === "currentLocale" ? value : undefined} />
|
|
113
131
|
{:else if active === "connection"}
|
|
114
|
-
|
|
132
|
+
|
|
115
133
|
{#if github}
|
|
116
134
|
<dl class="connection-details">
|
|
117
135
|
<dt>{$t("shell.githubAccount")}</dt>
|
|
@@ -165,17 +183,6 @@
|
|
|
165
183
|
|
|
166
184
|
</section>
|
|
167
185
|
|
|
168
|
-
{:else if active === "language"}
|
|
169
|
-
<h3>{$t("shell.language")}</h3>
|
|
170
|
-
<fieldset>
|
|
171
|
-
<legend>{$t("shell.editorInterfaceLanguage")}</legend>
|
|
172
|
-
{#each availableLocales as lang}
|
|
173
|
-
<label class="radio">
|
|
174
|
-
<input type="radio" bind:group={s.language} value={lang} />
|
|
175
|
-
<span>{localeNames[lang]}</span>
|
|
176
|
-
</label>
|
|
177
|
-
{/each}
|
|
178
|
-
</fieldset>
|
|
179
186
|
|
|
180
187
|
{:else}
|
|
181
188
|
<h3>{$t("shell.developerMode")}</h3>
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
import SidebarIcon from "./SidebarIcon.svelte";
|
|
5
5
|
import { tick } from "svelte";
|
|
6
6
|
import type { Manifest } from "../../manifest/types";
|
|
7
|
+
import { searchIndex, type SearchHit } from "../../manifest/search";
|
|
8
|
+
import type { SearchScope } from "../../config";
|
|
7
9
|
|
|
8
10
|
type Sel =
|
|
9
11
|
| { kind: "media" }
|
|
@@ -20,10 +22,14 @@
|
|
|
20
22
|
manifest,
|
|
21
23
|
sel,
|
|
22
24
|
onselect,
|
|
25
|
+
scope = "all",
|
|
26
|
+
contentLocale,
|
|
23
27
|
}: {
|
|
24
28
|
manifest: Manifest;
|
|
25
29
|
sel: Sel | null;
|
|
26
|
-
onselect: (s: Sel) => void;
|
|
30
|
+
onselect: (s: Sel, locale?: string) => void;
|
|
31
|
+
scope?: SearchScope;
|
|
32
|
+
contentLocale?: string;
|
|
27
33
|
} = $props();
|
|
28
34
|
|
|
29
35
|
let open = $state(false);
|
|
@@ -35,53 +41,11 @@
|
|
|
35
41
|
if (open) inputEl?.focus();
|
|
36
42
|
});
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
hint: string;
|
|
41
|
-
action: () => void;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const hits = $derived.by((): Hit[] => {
|
|
45
|
-
const q = query.trim().toLowerCase();
|
|
46
|
-
const all: Hit[] = [];
|
|
47
|
-
for (const p of manifest.pages) {
|
|
48
|
-
all.push({
|
|
49
|
-
label: p.label,
|
|
50
|
-
hint: $t("shell.page"),
|
|
51
|
-
action: () => onselect({ kind: "page", slug: p.slug }),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
for (const doc of manifest.documents ?? []) {
|
|
55
|
-
all.push({
|
|
56
|
-
label: String(doc.data.title ?? doc.data.name ?? doc.slug),
|
|
57
|
-
hint: $t("shell.searchItemHint", { name: doc.type, slug: doc.slug }),
|
|
58
|
-
action: () => onselect({ kind: "document", type: doc.type, id: doc.id }),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
for (const c of manifest.collections ?? []) {
|
|
62
|
-
all.push({
|
|
63
|
-
label: c.label,
|
|
64
|
-
hint: $t("shell.collection"),
|
|
65
|
-
action: () => onselect({ kind: "collection", name: c.name }),
|
|
66
|
-
});
|
|
67
|
-
for (const po of c.posts) {
|
|
68
|
-
all.push({
|
|
69
|
-
label: String(po.frontmatter.title ?? po.slug),
|
|
70
|
-
hint: $t("shell.searchItemHint", { name: c.label, slug: po.slug }),
|
|
71
|
-
action: () => onselect({ kind: "post", collection: c.name, slug: po.slug }),
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (manifest.menus) {
|
|
76
|
-
all.push({ label: $t("shell.menus"), hint: $t("shell.collection"), action: () => onselect({ kind: "menus" }) });
|
|
77
|
-
for (const m of manifest.menus.entries) all.push({ label: m.title, hint: $t("shell.searchMenuHint", { slug: m.slug }), action: () => onselect({ kind: "menu", slug: m.slug }) });
|
|
78
|
-
}
|
|
79
|
-
if (!q) return all.slice(0, 12);
|
|
80
|
-
return all.filter((h) => h.label.toLowerCase().includes(q) || h.hint.toLowerCase().includes(q)).slice(0, 12);
|
|
81
|
-
});
|
|
44
|
+
const index = $derived(searchIndex(manifest, scope, contentLocale, $t));
|
|
45
|
+
const hits = $derived(index.filter(hit => [hit.label, hit.hint, hit.language ?? ""].some(value => value.toLowerCase().includes(query.trim().toLowerCase()))).slice(0, 12));
|
|
82
46
|
|
|
83
|
-
function trigger(h:
|
|
84
|
-
h.
|
|
47
|
+
function trigger(h: SearchHit) {
|
|
48
|
+
onselect(h.selection, h.locale);
|
|
85
49
|
open = false;
|
|
86
50
|
query = "";
|
|
87
51
|
active = -1;
|
|
@@ -145,10 +109,10 @@
|
|
|
145
109
|
<kbd>{$t("shell.escapeKeyLowercase")}</kbd>
|
|
146
110
|
</div>
|
|
147
111
|
<ul id="rigel-search-results" role="listbox" aria-label={$t("shell.searchResults")}>
|
|
148
|
-
{#each hits as h, i (h.
|
|
112
|
+
{#each hits as h, i (h.id)}
|
|
149
113
|
<li role="presentation">
|
|
150
114
|
<button id={`rigel-search-hit-${i}`} role="option" aria-selected={active === i} onfocus={() => active = i} onclick={() => trigger(h)}>
|
|
151
|
-
<span>{h.label}</span>
|
|
115
|
+
<span>{h.label}{#if h.language}<span class="badge search-language" title={h.language} aria-label={h.language}>{h.locale?.slice(0, 2).toUpperCase()}</span>{/if}</span>
|
|
152
116
|
<span class="hint">{h.hint}</span>
|
|
153
117
|
</button>
|
|
154
118
|
</li>
|
|
@@ -3,6 +3,7 @@ import type { StockKeys } from "./images";
|
|
|
3
3
|
import { availableLocales, setLocale, type Locale } from "../i18n";
|
|
4
4
|
|
|
5
5
|
export interface RigelUserPref {
|
|
6
|
+
searchScope?: "all" | "currentLocale";
|
|
6
7
|
sidebarCollapsed?: boolean;
|
|
7
8
|
editorExperience?: "sidebar" | "inline";
|
|
8
9
|
previewSplit: number;
|
|
@@ -46,6 +47,7 @@ export function getSettings(): RigelSettings {
|
|
|
46
47
|
highContrast: typeof saved?.highContrast === "boolean" ? saved.highContrast : DEFAULTS.highContrast,
|
|
47
48
|
apiKeys: mergedKeys,
|
|
48
49
|
userPref: {
|
|
50
|
+
searchScope: saved?.userPref?.searchScope === "all" || saved?.userPref?.searchScope === "currentLocale" ? saved.userPref.searchScope : undefined,
|
|
49
51
|
sidebarCollapsed: saved?.userPref?.sidebarCollapsed === true,
|
|
50
52
|
editorExperience: saved?.userPref?.editorExperience === "sidebar" ? "sidebar" : DEFAULTS.userPref.editorExperience,
|
|
51
53
|
previewSplit: typeof saved?.userPref?.previewSplit === "number" && Number.isFinite(saved.userPref.previewSplit) && saved.userPref.previewSplit >= 0.2 && saved.userPref.previewSplit <= 0.8
|
package/src/editor/styles.css
CHANGED
|
@@ -408,6 +408,15 @@
|
|
|
408
408
|
.rigel .titlebar { display: flex; align-items: center; flex-wrap: wrap; gap: 0.75rem 1.5rem; }
|
|
409
409
|
.rigel .titlebar h2 { min-width: 0; overflow-wrap: anywhere; }
|
|
410
410
|
.rigel .titlebar > button { flex-shrink: 0; }
|
|
411
|
+
.rigel .pages-controls button[role="combobox"], .rigel .pages-search input[type="search"] { padding-top: 0.375rem; padding-bottom: 0.375rem; font-size: 0.8125rem; line-height: normal; }
|
|
412
|
+
.rigel .pages-controls { display: flex; align-items: center; justify-content: flex-end; flex-wrap: wrap; gap: 0.75rem; margin-left: auto; min-width: 0; max-width: 100%; }
|
|
413
|
+
.rigel .pages-controls .custom-select { max-width: 100%; }
|
|
414
|
+
.rigel .pages-search { position: relative; width: 240px; max-width: 100%; color: var(--text-muted); }
|
|
415
|
+
.rigel .pages-search > svg { position: absolute; left: 0.75rem; top: 50%; transform: translateY(-50%); pointer-events: none; }
|
|
416
|
+
.rigel .pages-search input[type="search"] { padding-left: 2.25rem; padding-right: 2.75rem; }
|
|
417
|
+
.rigel .pages-search input::-webkit-search-cancel-button { appearance: none; }
|
|
418
|
+
.rigel .pages-search .icon-btn { position: absolute; right: 0; top: 0; height: 100%; width: 2.5rem; }
|
|
419
|
+
|
|
411
420
|
.rigel .editor-actions { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; }
|
|
412
421
|
.rigel .editor-actions .back { display: flex; min-width: 0; margin: 0; }
|
|
413
422
|
.rigel .editor-actions .back button, .rigel .editor-actions .save-button { height: 2.25rem; padding: 0.5rem 0.75rem; border-width: 1px; border-style: solid; font-family: inherit; font-size: 0.8125rem; font-weight: 500; line-height: 1.25; }
|
|
@@ -567,6 +576,7 @@
|
|
|
567
576
|
.rigel .palette li button:hover,
|
|
568
577
|
.rigel .palette li button:focus-visible { background: var(--accent-soft); color: var(--on-accent-soft); }
|
|
569
578
|
.rigel .palette .hint { color: var(--text-muted); font-size: 0.75rem; font-weight: 400; }
|
|
579
|
+
.rigel .palette .search-language { margin-left: 0.5rem; }
|
|
570
580
|
.rigel .palette li button[aria-selected="true"] .hint,
|
|
571
581
|
.rigel .palette li button:hover .hint,
|
|
572
582
|
.rigel .palette li button:focus-visible .hint { color: var(--on-accent-soft); }
|
package/src/index.ts
CHANGED
|
@@ -115,7 +115,7 @@ export { validateMenu } from "./manifest/menu";
|
|
|
115
115
|
export type { MenuItem, MenuDocument } from "./manifest/menu";
|
|
116
116
|
export { defineCopy } from "./manifest/types";
|
|
117
117
|
export type { CopyField, CopyGroup, CopyPage, Manifest, CollectionField } from "./manifest/types";
|
|
118
|
-
export type { RigelConfig, CollectionConfig, ContentLocalesConfig } from "./config";
|
|
118
|
+
export type { RigelConfig, CollectionConfig, ContentLocalesConfig, SearchScope } from "./config";
|
|
119
119
|
export type { JsonOutput } from "./json-output";
|
|
120
120
|
export type { ReferenceOption } from "./manifest/references";
|
|
121
121
|
export type { SidebarEntry, SidebarTarget, SidebarIconName } from "./manifest/navigation";
|
package/src/manifest/pages.ts
CHANGED
|
@@ -8,10 +8,6 @@ export function pageTemplates(manifest: Manifest) {
|
|
|
8
8
|
return (manifest.schema?.documents ?? []).filter(type => !type.shared && !type.singleton && type.creatable !== false);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function defaultPageCategory(manifest: Manifest): "" | null {
|
|
12
|
-
return [...manifest.pages, ...(manifest.schema?.documents ?? [])].some(entry => !entry.shared && entry.category === undefined) ? "" : null;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
11
|
export function inPageCategory(category: string | undefined, filter: string | null): boolean {
|
|
16
12
|
return filter === null || (category ?? "") === filter;
|
|
17
13
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { SearchScope } from "../config";
|
|
2
|
+
import type { Manifest } from "./types";
|
|
3
|
+
import type { Selection } from "./navigation";
|
|
4
|
+
import { contentForLocale } from "./content-locales";
|
|
5
|
+
|
|
6
|
+
export interface SearchHit {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
hint: string;
|
|
10
|
+
locale?: string;
|
|
11
|
+
language?: string;
|
|
12
|
+
selection: Selection;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function effectiveSearchScope(config: Manifest["search"], override: unknown): SearchScope {
|
|
16
|
+
return override === "all" || override === "currentLocale" ? override : config?.scope === "currentLocale" ? "currentLocale" : "all";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function searchIndex(manifest: Manifest, scope: SearchScope, locale: string | undefined, t: (key: string, params?: Record<string, string>) => string): SearchHit[] {
|
|
20
|
+
const content = scope === "currentLocale" ? contentForLocale(manifest, locale) : manifest;
|
|
21
|
+
const hits = new Map<string, SearchHit>();
|
|
22
|
+
function add(selection: Selection, label: string, hint: string, code?: string) {
|
|
23
|
+
const id = JSON.stringify(selection);
|
|
24
|
+
const language = manifest.contentLocales && manifest.contentLocales.locales.length > 1 ? manifest.contentLocales.locales.find(entry => entry.code === code)?.label : undefined;
|
|
25
|
+
if (!hits.has(id)) hits.set(id, { id, label, hint, selection, locale: code, language });
|
|
26
|
+
}
|
|
27
|
+
for (const page of content.pages) add({ kind: "page", slug: page.slug }, page.label, t("shell.page"), page.locale);
|
|
28
|
+
for (const doc of content.documents ?? []) {
|
|
29
|
+
const type = content.schema?.documents.find(type => type.name === doc.type);
|
|
30
|
+
add({ kind: "document", type: doc.type, id: doc.id }, String(doc.data.title ?? doc.data.name ?? doc.slug), t("shell.searchItemHint", { name: doc.type, slug: doc.slug }), type?.locale);
|
|
31
|
+
}
|
|
32
|
+
for (const collection of content.collections ?? []) {
|
|
33
|
+
add({ kind: "collection", name: collection.name }, collection.label, t("shell.collection"), collection.locale);
|
|
34
|
+
for (const post of collection.posts) add({ kind: "post", collection: collection.name, slug: post.slug }, String(post.frontmatter.title ?? post.slug), t("shell.searchItemHint", { name: collection.label, slug: post.slug }), collection.locale);
|
|
35
|
+
}
|
|
36
|
+
if (content.menus) {
|
|
37
|
+
add({ kind: "menus" }, t("shell.menus"), t("shell.collection"));
|
|
38
|
+
for (const menu of content.menus.entries) add({ kind: "menu", slug: menu.slug }, menu.title, t("shell.searchMenuHint", { slug: menu.slug }));
|
|
39
|
+
}
|
|
40
|
+
return [...hits.values()];
|
|
41
|
+
}
|
package/src/manifest/types.ts
CHANGED
|
@@ -38,6 +38,7 @@ export interface GitHubBackendConfig {
|
|
|
38
38
|
|
|
39
39
|
export interface Manifest {
|
|
40
40
|
contentLocales?: import("../config").ContentLocalesConfig;
|
|
41
|
+
search?: { scope: import("../config").SearchScope };
|
|
41
42
|
backend?: "github";
|
|
42
43
|
github?: GitHubBackendConfig;
|
|
43
44
|
sidebar?: import("./navigation").SidebarEntry[];
|
|
@@ -169,6 +169,7 @@ export const menus = {
|
|
|
169
169
|
demo: cfg.demo,
|
|
170
170
|
languages: cfg.languages,
|
|
171
171
|
contentLocales: cfg.contentLocales,
|
|
172
|
+
search: cfg.search,
|
|
172
173
|
publicDir: publicDir?.href,
|
|
173
174
|
mediaDir: cfg.mediaDir,
|
|
174
175
|
maxFileSize: cfg.maxFileSize,
|
|
@@ -218,7 +219,7 @@ ${collectionCode}
|
|
|
218
219
|
];
|
|
219
220
|
for (const collection of collections) __rigelAssertUniqueUrlSlugs(collection.posts.map(post => ({ slug: post.slug, values: post.frontmatter })));
|
|
220
221
|
${menuCode}
|
|
221
|
-
export default { pages, collections, documents${cfg.contentLocales ? ", contentLocales: " + JSON.stringify(cfg.contentLocales) : ""}${github ? ', backend: "github", github: ' + JSON.stringify(github) : ""}${cfg.menus ? ", menus" : ""}${cfg.schema ? ", schema: " + JSON.stringify(cfg.schema) : ""}${cfg.preview ? ", preview: " + JSON.stringify(cfg.preview) : ""}${cfg.sidebar ? ", sidebar: " + JSON.stringify(cfg.sidebar) : ""} };
|
|
222
|
+
export default { pages, collections, documents, search: ${JSON.stringify(cfg.search)}${cfg.contentLocales ? ", contentLocales: " + JSON.stringify(cfg.contentLocales) : ""}${github ? ', backend: "github", github: ' + JSON.stringify(github) : ""}${cfg.menus ? ", menus" : ""}${cfg.schema ? ", schema: " + JSON.stringify(cfg.schema) : ""}${cfg.preview ? ", preview: " + JSON.stringify(cfg.preview) : ""}${cfg.sidebar ? ", sidebar: " + JSON.stringify(cfg.sidebar) : ""} };
|
|
222
223
|
`;
|
|
223
224
|
},
|
|
224
225
|
};
|