@rigel-cms/rigel 1.2.2 → 1.4.0-rc.1

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.
Files changed (44) hide show
  1. package/docs/content-locales.md +8 -0
  2. package/package.json +1 -1
  3. package/src/editor/App.svelte +52 -28
  4. package/src/editor/i18n/de/fields.json +3 -0
  5. package/src/editor/i18n/de/media.json +9 -1
  6. package/src/editor/i18n/de/shell.json +4 -0
  7. package/src/editor/i18n/en/fields.json +3 -0
  8. package/src/editor/i18n/en/media.json +9 -1
  9. package/src/editor/i18n/en/shell.json +4 -0
  10. package/src/editor/i18n/es/fields.json +3 -0
  11. package/src/editor/i18n/es/media.json +9 -1
  12. package/src/editor/i18n/es/shell.json +4 -0
  13. package/src/editor/i18n/fr/fields.json +3 -0
  14. package/src/editor/i18n/fr/media.json +9 -1
  15. package/src/editor/i18n/fr/shell.json +4 -0
  16. package/src/editor/i18n/it/fields.json +3 -0
  17. package/src/editor/i18n/it/media.json +9 -1
  18. package/src/editor/i18n/it/shell.json +4 -0
  19. package/src/editor/i18n/ja/fields.json +3 -0
  20. package/src/editor/i18n/ja/media.json +9 -1
  21. package/src/editor/i18n/ja/shell.json +4 -0
  22. package/src/editor/i18n/nl/fields.json +3 -0
  23. package/src/editor/i18n/nl/media.json +9 -1
  24. package/src/editor/i18n/nl/shell.json +4 -0
  25. package/src/editor/i18n/ru/fields.json +3 -0
  26. package/src/editor/i18n/ru/media.json +9 -1
  27. package/src/editor/i18n/ru/shell.json +4 -0
  28. package/src/editor/i18n/zh/fields.json +3 -0
  29. package/src/editor/i18n/zh/media.json +9 -1
  30. package/src/editor/i18n/zh/shell.json +4 -0
  31. package/src/editor/lib/ActionDialog.svelte +23 -3
  32. package/src/editor/lib/ImagePicker.svelte +27 -19
  33. package/src/editor/lib/MarkdownEditor.svelte +7 -5
  34. package/src/editor/lib/SchemaField.svelte +2 -2
  35. package/src/editor/lib/SchemaPanel.svelte +76 -4
  36. package/src/editor/lib/VisualEditor.svelte +38 -3
  37. package/src/editor/lib/icons.ts +4 -0
  38. package/src/editor/styles.css +15 -3
  39. package/src/manifest/content-locales.ts +1 -1
  40. package/src/manifest/navigation.ts +4 -3
  41. package/src/manifest/pages.ts +17 -0
  42. package/src/media-types.ts +17 -0
  43. package/src/schema/types.ts +3 -1
  44. package/src/schema/validate.ts +8 -3
@@ -51,3 +51,11 @@ Content without a `locale` remains visible in every language. Use this for commo
51
51
  Without `contentLocales`, the editor keeps its existing navigation and shows no content-language dropdown. A single configured locale also needs no dropdown.
52
52
 
53
53
  Your Astro routes and templates still own website localization, including public language links, HTML `lang`, translated navigation and fallback behavior. Rigel does not translate text, pair translated entries or generate localized routes. Keep each type's URL mapping consistent with the website routes, and prepare its preview bindings as usual.
54
+
55
+ ## Page categories and templates
56
+
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 Pages table offers Core, All pages and each category visible in the selected content language. Core is the initial filter when uncategorised pages or types exist; category-only sites initially show All pages. Back returns to the originating table/filter, not a filter inferred from the opened page.
58
+
59
+ 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
+
61
+ A visual variant can be another document definition with its own category, defaults and template—for example a Resources category with a dark-theme initial value. Its native type, folder, preview mapping and host renderer must be configured normally. A category does not change the editor theme, rewrite existing document types or generate website routes.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rigel-cms/rigel",
3
- "version": "1.2.2",
3
+ "version": "1.4.0-rc.1",
4
4
  "description": "A CMS for Astro sites. You edit the text, Git keeps the history.",
5
5
  "keywords": [
6
6
  "astro",
@@ -15,6 +15,7 @@
15
15
  import TopBar from "./lib/TopBar.svelte";
16
16
  import Select from "./lib/Select.svelte";
17
17
  import { contentForLocale, selectionInContent } from "../manifest/content-locales";
18
+ import { pageCategories, pageTemplates, inPageCategory, defaultPageCategory } from "../manifest/pages";
18
19
  import { validateDocumentSlugs } from "../slugs";
19
20
  import Shortcuts from "./lib/Shortcuts.svelte";
20
21
  import CollectionPanel from "./lib/CollectionPanel.svelte";
@@ -38,7 +39,7 @@
38
39
  import VisualEditor from "./lib/VisualEditor.svelte";
39
40
  import { newDocumentFromTemplate } from "../schema/paths";
40
41
  import { previewUrl, previewPath } from "../preview";
41
- import type { ContentDocument, Schema } from "../schema/types";
42
+ import type { ContentDocument, DocumentDef, Schema } from "../schema/types";
42
43
  import { buildSidebar, navigationKey, type Selection } from "../manifest/navigation";
43
44
  import { startGitHubLogin, checkGitHubAccess, type GitHubAccess } from "./lib/github-auth";
44
45
  import { loadManifestRemote, saveRemoteSelection, deleteRemotePost, reviewRemotePublish, confirmRemotePublish, PublishReviewChangedError, type GitContext, type PublishReview } from "./lib/github-backend";
@@ -142,7 +143,7 @@
142
143
  }
143
144
  let pendingNavigation = $state<(() => void) | null>(null);
144
145
  let pendingNavigationCancel: (() => void) | null = null;
145
- let actionDialog = $state<{ title: string; description: string; confirmLabel: string; fieldLabel?: string; danger?: boolean; onconfirm: (value: string) => void | Promise<void> } | null>(null);
146
+ let actionDialog = $state<{ title: string; description: string; confirmLabel: string; fieldLabel?: string; danger?: boolean; templates?: DocumentDef[]; templateCategory?: string; onconfirm: (value: string, type?: string) => void | Promise<void> } | null>(null);
146
147
  let newEntry = $state<{ collection: string; post: CollectionPost } | null>(null);
147
148
  let newDocument = $state<{ type: string; id: string; slug: string } | null>(null);
148
149
  let docBase = $state<Record<string, unknown> | null>(null);
@@ -159,6 +160,7 @@
159
160
  if (!Array.isArray(data.pages) || (data.backend === "github" && !data.github)) throw new Error($t("shell.configurationIncomplete"));
160
161
  blueprint = data;
161
162
  manifest = data;
163
+ pagesFilter = defaultPageCategory(contentForLocale(data, data.contentLocales?.defaultLocale));
162
164
  ready = true;
163
165
  if (data.backend === "github" && data.github) {
164
166
  const session = readSession(data.github);
@@ -449,7 +451,7 @@
449
451
 
450
452
  const columns = $derived.by((): { key: string; label: string; image?: boolean }[] => {
451
453
  if (sel?.kind === "menus") return [{ key: "title", label: $t("shell.name") }, { key: "slug", label: $t("shell.slug") }];
452
- if (sel?.kind === "pages") return [{ key: "label", label: $t("shell.title") }, { key: "slug", label: $t("shell.slug") }, { key: "category", label: $t("shell.category") }];
454
+ if (sel?.kind === "pages") return [{ key: "label", label: $t("shell.title") }, { key: "slug", label: $t("shell.slug") }, { key: "path", label: $t("shell.path") }, { key: "category", label: $t("shell.category") }];
453
455
  const source = sel?.kind === "documents" ? activeDocType : coll;
454
456
  if (!source) return [];
455
457
  const keys = ["title", "slug", ...(source.columns ?? (sel?.kind === "documents" ? [] : source.fields.filter(field => !isSeoField(field.key)).map(field => field.key)))];
@@ -478,11 +480,18 @@
478
480
  });
479
481
 
480
482
  const sidebar = $derived(buildSidebar(visibleContent, $t));
481
- const pageCategory = $derived(sel?.kind === "pages" ? sel.category : undefined);
483
+ let pagesFilter = $state<string | null>("");
484
+ let entryList = $state<Sel | null>(null);
485
+ const categories = $derived(pageCategories(visibleContent));
486
+ const pageCategory = $derived(sel?.kind === "pages" && sel.category !== undefined ? sel.category : pagesFilter);
482
487
  const schema = $derived<Schema | undefined>(manifest.schema);
483
488
  const docTypes = $derived(visibleContent.schema?.documents ?? []);
484
489
 
485
490
  const pageDocType = $derived(docTypes.find((type) => !type.shared && !type.singleton));
491
+ const templates = $derived(pageTemplates(visibleContent));
492
+ const pageBack = $derived<Extract<Sel, { kind: "pages" }>>(entryList?.kind === "pages" ? entryList : { kind: "pages" });
493
+ const documentBack = $derived<Sel | null>(entryList?.kind === "documents" && entryList.type === activeDocType?.name ? entryList : !activeDocType?.shared ? pageBack : activeDocType.singleton ? null : { kind: "documents", type: activeDocType.name });
494
+ const pageBackLabel = $derived((pageBack.category ?? pagesFilter) || (pagesFilter === "" && categories.length ? $t("shell.coreCategory") : $t("shell.pages")));
486
495
  const pageDocuments = $derived((visibleContent.documents ?? []).filter((doc) => docTypes.some(type => type.name === doc.type && !type.shared)));
487
496
  function docTypeName(s: Sel | null): string | null {
488
497
  return s && (s.kind === "document" || s.kind === "documents") ? s.type : null;
@@ -509,12 +518,13 @@
509
518
 
510
519
  const sortedPages = $derived.by(() => {
511
520
  const rows = [
512
- ...visibleContent.pages.filter((p) => !p.shared && (pageCategory === undefined || p.category === pageCategory))
513
- .map((p) => ({ slug: p.slug, title: p.label, category: p.category, draft: false, target: { kind: "page", slug: p.slug } as Sel })),
514
- ...(pageCategory === undefined ? pageDocuments
515
- .map((doc) => ({ slug: effectiveSlug(doc.data, doc.slug), title: String(doc.data.title ?? doc.slug), category: undefined, draft: doc.data.draft === true, target: { kind: "document", type: doc.type, id: doc.id } as Sel })) : []),
521
+ ...visibleContent.pages.filter((p) => !p.shared && inPageCategory(p.category, pageCategory))
522
+ .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
+ ...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 }))
525
+ .filter(doc => inPageCategory(doc.category, pageCategory)),
516
526
  ];
517
- const key = sortBy === "slug" || sortBy === "category" ? sortBy : "title";
527
+ const key = sortBy === "slug" || sortBy === "category" || sortBy === "path" ? sortBy : "title";
518
528
  return rows.sort((a, b) => String(a[key] ?? "").localeCompare(String(b[key] ?? "")) * sortDir);
519
529
  });
520
530
  function documentCell(doc: ContentDocument, key: string): string {
@@ -525,13 +535,13 @@
525
535
 
526
536
  const menu = $derived.by(() => { const slug = sel?.kind === "menu" ? sel.slug : null; return manifest.menus?.entries.find((m) => m.slug === slug); });
527
537
  const sortedMenus = $derived([...(manifest.menus?.entries ?? [])].sort((a, b) => (sortBy === "slug" ? a.slug.localeCompare(b.slug) : a.title.localeCompare(b.title)) * sortDir));
528
- const listTitle = $derived(sel?.kind === "menus" ? $t("shell.allMenus") : sel?.kind === "documents" ? activeDocType?.title ?? $t("shell.documents") : sel?.kind === "pages" ? pageCategory !== undefined ? $t("shell.allNamedEntries", { name: pageCategory }) : $t("shell.allPages") : coll?.listTitle?.trim() || (coll?.label !== undefined ? $t("shell.allNamedEntries", { name: coll.label }) : $t("shell.allEntries")));
538
+ const listTitle = $derived(sel?.kind === "menus" ? $t("shell.allMenus") : sel?.kind === "documents" ? activeDocType?.title ?? $t("shell.documents") : sel?.kind === "pages" ? pageCategory !== null && categories.length ? $t("shell.allNamedEntries", { name: pageCategory || $t("shell.coreCategory") }) : $t("shell.allPages") : coll?.listTitle?.trim() || (coll?.label !== undefined ? $t("shell.allNamedEntries", { name: coll.label }) : $t("shell.allEntries")));
529
539
  const listRows = $derived(sel?.kind === "menus"
530
540
  ? sortedMenus.map((m) => ({ slug: m.slug, cells: { slug: m.slug, title: m.title } as Record<string, string>, draft: false, target: { kind: "menu", slug: m.slug } as Sel }))
531
541
  : sel?.kind === "documents"
532
542
  ? typeDocuments.map((doc) => ({ slug: doc.slug, cells: Object.fromEntries(columns.map(column => [column.key, documentCell(doc, column.key)])), draft: doc.data.draft === true, target: { kind: "document", type: doc.type, id: doc.id } as Sel }))
533
543
  : sel?.kind === "pages"
534
- ? sortedPages.map((p) => ({ slug: p.slug, cells: { label: p.title, slug: p.slug, category: p.category ?? "—" } as Record<string, string>, draft: p.draft, target: p.target }))
544
+ ? sortedPages.map((p) => ({ slug: p.slug, cells: { label: p.title, slug: p.slug, path: p.path ?? "—", category: p.category ?? (categories.length ? $t("shell.coreCategory") : "—") } as Record<string, string>, draft: p.draft, target: p.target }))
535
545
  : sortedPosts.map((p) => ({ slug: p.slug, cells: Object.fromEntries(columns.map((c) => [c.key, cellValue(p, c.key)])), draft: Boolean(p.frontmatter.draft), target: { kind: "post", collection: coll!.name, slug: p.slug } as Sel })));
536
546
  const titleColumn = $derived(sel?.kind === "pages" ? "label" : "title");
537
547
 
@@ -589,9 +599,10 @@
589
599
  if (target.kind === "menus") return sel.kind === "menu";
590
600
  if (target.kind === "documents") return sel.kind === "document" && sel.type === target.type;
591
601
  if (target.kind === "pages") {
592
- if (sel.kind === "page") return !page?.shared && (target.category ? page?.category === target.category : !page?.category);
593
- const typeName = sel.kind === "document" ? sel.type : null;
594
- return !target.category && docTypes.some(type => type.name === typeName && !type.shared);
602
+ const entry = sel.kind === "page" ? page : sel.kind === "document" ? docTypes.find(type => type.name === sel.type) : undefined;
603
+ if (!entry || entry.shared) return false;
604
+ const category = sidebar.some(node => node.kind === "link" && node.target.kind === "pages" && node.target.category === entry.category) ? entry.category : undefined;
605
+ return target.category === category;
595
606
  }
596
607
  return false;
597
608
  }
@@ -635,6 +646,11 @@
635
646
  }
636
647
 
637
648
  function openSelection(next: Sel | null) {
649
+ if (next?.kind === "page" || next?.kind === "document") {
650
+ if (sel?.kind === "pages" || sel?.kind === "documents") entryList = sel;
651
+ else if (sel?.kind !== "page" && sel?.kind !== "document") entryList = null;
652
+ }
653
+ if (next?.kind === "pages" && next.category !== undefined) pagesFilter = next.category;
638
654
  sel = next;
639
655
  saveError = "";
640
656
  sortBy = "";
@@ -684,6 +700,8 @@
684
700
  requestNavigation(() => {
685
701
  const destination = contentForLocale(manifest, value);
686
702
  selectedContentLocale = value;
703
+ if (pagesFilter && !pageCategories(destination).includes(pagesFilter)) pagesFilter = defaultPageCategory(destination);
704
+ if (!selectionInContent(destination, entryList)) entryList = null;
687
705
  if (!selectionInContent(destination, sel)) openSelection(null);
688
706
  focusContentLocale();
689
707
  }, focusContentLocale);
@@ -724,21 +742,25 @@
724
742
  return String(v);
725
743
  }
726
744
 
727
- function newPage(type = pageDocType) {
745
+ function newPage(type = pageDocType, chooseTemplate = false) {
746
+ if (chooseTemplate) type = templates.find(type => inPageCategory(type.category, pageCategory)) ?? templates[0];
728
747
  if (!type || !schema || type.creatable === false || type.singleton) return;
729
748
  actionDialog = {
730
- title: $t("shell.addDocument", { name: type.title }), description: $t("shell.newDocumentDescription"), fieldLabel: type.shared ? $t("shell.name") : $t("shell.pageName"), confirmLabel: $t("shell.createDraft"),
731
- onconfirm: (title) => {
749
+ title: $t("shell.addDocument", { name: chooseTemplate ? $t("shell.pages") : type.title }), description: $t("shell.newDocumentDescription"), fieldLabel: type.shared ? $t("shell.name") : $t("shell.pageName"), confirmLabel: $t("shell.createDraft"),
750
+ ...(chooseTemplate ? { templates, templateCategory: type.category ?? "" } : {}),
751
+ onconfirm: (title, selectedType) => {
752
+ const selected = chooseTemplate ? templates.find(entry => entry.name === selectedType) : type;
753
+ if (!selected) throw new Error($t("shell.chooseTemplate"));
732
754
  const slug = slugify(title);
733
755
  if (!slug) throw new Error($t("shell.titleRequired"));
734
- if (manifest.documents?.some(doc => doc.type === type.name && doc.slug === slug)) throw new Error($t("shell.slugAlreadyUsed", { slug }));
735
- const id = `${type.name}:${slug}`;
736
- const data = newDocumentFromTemplate(schema, type.name, title, slug);
737
- validateDocumentSlugs(schema, [...(manifest.documents ?? []), { id, type: type.name, slug, data }], manifest.preview, $t);
738
- newDocument = { type: type.name, id, slug };
756
+ if (manifest.documents?.some(doc => doc.type === selected.name && doc.slug === slug)) throw new Error($t("shell.slugAlreadyUsed", { slug }));
757
+ const id = `${selected.name}:${slug}`;
758
+ const data = newDocumentFromTemplate(schema, selected.name, title, slug);
759
+ validateDocumentSlugs(schema, [...(manifest.documents ?? []), { id, type: selected.name, slug, data }], manifest.preview, $t);
760
+ newDocument = { type: selected.name, id, slug };
739
761
  draft = JSON.parse(JSON.stringify(data));
740
762
  docBase = null;
741
- openSelection({ kind: "document", type: type.name, id });
763
+ openSelection({ kind: "document", type: selected.name, id });
742
764
  },
743
765
  };
744
766
  }
@@ -955,10 +977,9 @@
955
977
  {#if sel.kind === "page" || sel.kind === "post" || sel.kind === "menu" || sel.kind === "document"}
956
978
  <div class="editor-actions">
957
979
  {#if sel.kind === "page" && page}
958
- <p class="back"><button onclick={() => select(page.shared ? null : { kind: "pages", category: page.category })}>{$t("shell.backTo", { name: page.shared ? $t("shell.welcome") : page.category ?? $t("shell.pages") })}</button></p>
980
+ <p class="back"><button onclick={() => select(page.shared ? null : pageBack)}>{$t("shell.backTo", { name: page.shared ? $t("shell.welcome") : pageBackLabel })}</button></p>
959
981
  {:else if sel.kind === "document"}
960
- {@const backSel: Sel | null = activeDocType?.singleton ? null : sel.type === pageDocType?.name ? { kind: "pages" } : { kind: "documents", type: sel.type }}
961
- <p class="back"><button onclick={() => select(backSel)}>{$t("shell.backTo", { name: activeDocType?.singleton ? $t("shell.welcome") : sel.type === pageDocType?.name ? $t("shell.pages") : activeDocType?.title ?? $t("shell.documents") })}</button></p>
982
+ <p class="back"><button onclick={() => select(documentBack)}>{$t("shell.backTo", { name: !documentBack ? $t("shell.welcome") : documentBack.kind === "pages" ? pageBackLabel : activeDocType?.title ?? $t("shell.documents") })}</button></p>
962
983
  {:else if sel.kind === "post" && coll}
963
984
  <p class="back"><button onclick={() => select({ kind: "collection", name: coll.name })}>{$t("shell.backTo", { name: coll.label })}</button></p>
964
985
  {:else if sel.kind === "menu"}
@@ -974,8 +995,11 @@
974
995
  {/if}
975
996
  <div class="titlebar">
976
997
  <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>
977
- {#if (sel?.kind === "collection" && coll) || (sel?.kind === "pages" && pageDocType?.creatable !== false && pageDocType) || (sel?.kind === "documents" && activeDocType && activeDocType.creatable !== false && !activeDocType.singleton)}
978
- <button class="primary" onclick={() => (sel?.kind === "collection" ? newPost(coll!) : newPage(sel?.kind === "documents" ? activeDocType : pageDocType))}>{$t("shell.addNew")}</button>
998
+ {#if sel.kind === "pages" && categories.length}
999
+ <Select label={$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" }); } }} />
1000
+ {/if}
1001
+ {#if (sel?.kind === "collection" && coll) || (sel?.kind === "pages" && templates.length) || (sel?.kind === "documents" && activeDocType && activeDocType.creatable !== false && !activeDocType.singleton)}
1002
+ <button class="primary" onclick={() => (sel?.kind === "collection" ? newPost(coll!) : newPage(sel?.kind === "documents" ? activeDocType : pageDocType, sel?.kind === "pages"))}>{$t("shell.addNew")}</button>
979
1003
  {/if}
980
1004
  {#if sel?.kind === "post" && post}
981
1005
  <button class="icon-btn" aria-label={$t("shell.deletePost")} data-tooltip={$t("shell.deletePost")} disabled={saveState === "saving"} onclick={deletePost}>
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Ändert die URL, nicht die Dokumentkennung oder den Dateinamen. Die Website-Route muss die Slug-Metadaten verwenden.",
31
31
  "fields.newPageNotice": "Neue Seite. Beim Speichern wird die JSON-Datei geschrieben. Die Route ist verfügbar, sobald die Datei auf der Website vorliegt.",
32
32
  "fields.structure": "Struktur",
33
+ "fields.reorderSection": "{label} neu anordnen",
34
+ "fields.sectionReorderHint": "Zum Anordnen ziehen oder die Pfeiltasten nach oben und unten verwenden. Escape bricht das Ziehen ab. Änderungen müssen gespeichert werden.",
35
+ "fields.sectionMoved": "{label} an Position {position} von {count} verschoben.",
33
36
  "fields.moveSectionUp": "{label} nach oben verschieben",
34
37
  "fields.moveSectionDown": "{label} nach unten verschieben",
35
38
  "fields.duplicateSection": "{label} duplizieren",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Fotoimport fehlgeschlagen ({status})",
163
163
  "media.invalidImportedUrl": "Ungültige URL des importierten Fotos",
164
164
  "media.providerVideo": "Video von {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Datei auswählen",
167
+ "media.removeFile": "Datei entfernen",
168
+ "media.noFile": "Keine Datei",
169
+ "media.useFile": "Datei verwenden",
170
+ "media.fileSources": "Dateiquellen",
171
+ "media.uploadingFile": "Datei wird hochgeladen…",
172
+ "media.chooseAcceptedFile": "Wählen Sie eine unterstützte Datei passend zu: {accept}.",
173
+ "media.uploadFailedGeneric": "Datei-Upload fehlgeschlagen"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Standard",
3
+ "shell.template": "Vorlage",
4
+ "shell.chooseTemplate": "Vorlage auswählen",
2
5
  "shell.search": "Suchen",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Dialog schließen",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Daten löschen",
95
98
  "shell.eraseBrowserDataDescription": "Dadurch werden Rigel-Einstellungen, API-Schlüssel, die GitHub-Anmeldung und ungespeicherte Entwürfe aus diesem Browser entfernt. Gespeicherte Repository-Dateien bleiben unverändert. Der Editor wird neu geladen.",
96
99
  "shell.name": "Name",
100
+ "shell.path": "Pfad",
97
101
  "shell.slug": "Slug",
98
102
  "shell.title": "Titel",
99
103
  "shell.category": "Kategorie",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Changes the URL, not the document identifier or filename. The website route must use the slug metadata.",
31
31
  "fields.newPageNotice": "New page. Save writes the JSON file; the route appears once the site has the file.",
32
32
  "fields.structure": "Structure",
33
+ "fields.reorderSection": "Reorder {label}",
34
+ "fields.sectionReorderHint": "Drag to reorder, or use Arrow Up and Arrow Down. Escape cancels dragging. Changes require Save.",
35
+ "fields.sectionMoved": "{label} moved to position {position} of {count}.",
33
36
  "fields.moveSectionUp": "Move {label} up",
34
37
  "fields.moveSectionDown": "Move {label} down",
35
38
  "fields.duplicateSection": "Duplicate {label}",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Photo import failed ({status})",
163
163
  "media.invalidImportedUrl": "Invalid imported photo URL",
164
164
  "media.providerVideo": "{provider} Video",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Choose file",
167
+ "media.removeFile": "Remove file",
168
+ "media.noFile": "No file",
169
+ "media.useFile": "Use file",
170
+ "media.fileSources": "File sources",
171
+ "media.uploadingFile": "Uploading file…",
172
+ "media.chooseAcceptedFile": "Choose a supported file matching: {accept}.",
173
+ "media.uploadFailedGeneric": "File upload failed"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Core",
3
+ "shell.template": "Template",
4
+ "shell.chooseTemplate": "Choose a template",
2
5
  "shell.search": "Search",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Close dialog",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Erase data",
95
98
  "shell.eraseBrowserDataDescription": "This removes Rigel settings, API keys, GitHub login and unsaved drafts from this browser. Saved repository files will not change. The editor will reload.",
96
99
  "shell.name": "Name",
100
+ "shell.path": "Path",
97
101
  "shell.slug": "Slug",
98
102
  "shell.title": "Title",
99
103
  "shell.category": "Category",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Cambia la URL, no el identificador del documento ni el nombre del archivo. La ruta del sitio web debe usar el slug de los metadatos.",
31
31
  "fields.newPageNotice": "Página nueva. Al pulsar Guardar se escribe el archivo JSON; la ruta aparece cuando el sitio dispone del archivo.",
32
32
  "fields.structure": "Estructura",
33
+ "fields.reorderSection": "Reordenar {label}",
34
+ "fields.sectionReorderHint": "Arrastra para reordenar o usa las flechas arriba y abajo. Escape cancela el arrastre. Los cambios requieren Guardar.",
35
+ "fields.sectionMoved": "{label} se ha movido a la posición {position} de {count}.",
33
36
  "fields.moveSectionUp": "Subir la sección «{label}»",
34
37
  "fields.moveSectionDown": "Bajar la sección «{label}»",
35
38
  "fields.duplicateSection": "Duplicar la sección «{label}»",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "No se pudo importar la foto ({status})",
163
163
  "media.invalidImportedUrl": "URL de la foto importada no válida",
164
164
  "media.providerVideo": "Video de {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Elegir archivo",
167
+ "media.removeFile": "Quitar archivo",
168
+ "media.noFile": "Sin archivo",
169
+ "media.useFile": "Usar archivo",
170
+ "media.fileSources": "Fuentes de archivos",
171
+ "media.uploadingFile": "Subiendo archivo…",
172
+ "media.chooseAcceptedFile": "Elige un archivo compatible con: {accept}.",
173
+ "media.uploadFailedGeneric": "Error al subir el archivo"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Principales",
3
+ "shell.template": "Plantilla",
4
+ "shell.chooseTemplate": "Elige una plantilla",
2
5
  "shell.search": "Buscar",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Cerrar diálogo",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Borrar datos",
95
98
  "shell.eraseBrowserDataDescription": "Se eliminarán de este navegador la configuración de Rigel, las claves de API, el inicio de sesión de GitHub y los borradores sin guardar. Los archivos guardados en el repositorio no cambiarán. El editor se volverá a cargar.",
96
99
  "shell.name": "Nombre",
100
+ "shell.path": "Ruta",
97
101
  "shell.slug": "Slug",
98
102
  "shell.title": "Título",
99
103
  "shell.category": "Categoría",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Modifie l’URL, pas l’identifiant du document ni le nom du fichier. La route du site doit utiliser la métadonnée slug.",
31
31
  "fields.newPageNotice": "Nouvelle page. Enregistrer écrit le fichier JSON ; la route apparaît une fois le fichier présent sur le site.",
32
32
  "fields.structure": "Structure",
33
+ "fields.reorderSection": "Réordonner {label}",
34
+ "fields.sectionReorderHint": "Faites glisser pour réordonner, ou utilisez les flèches haut et bas. Échap annule le déplacement. Les modifications nécessitent Enregistrer.",
35
+ "fields.sectionMoved": "{label} déplacé à la position {position} sur {count}.",
33
36
  "fields.moveSectionUp": "Monter la section « {label} »",
34
37
  "fields.moveSectionDown": "Descendre la section « {label} »",
35
38
  "fields.duplicateSection": "Dupliquer la section « {label} »",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Échec de l’importation de la photo ({status})",
163
163
  "media.invalidImportedUrl": "URL de la photo importée invalide",
164
164
  "media.providerVideo": "Vidéo {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Choisir un fichier",
167
+ "media.removeFile": "Retirer le fichier",
168
+ "media.noFile": "Aucun fichier",
169
+ "media.useFile": "Utiliser le fichier",
170
+ "media.fileSources": "Sources de fichiers",
171
+ "media.uploadingFile": "Téléversement du fichier…",
172
+ "media.chooseAcceptedFile": "Choisissez un fichier pris en charge correspondant à : {accept}.",
173
+ "media.uploadFailedGeneric": "Échec du téléversement"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Principales",
3
+ "shell.template": "Modèle",
4
+ "shell.chooseTemplate": "Choisir un modèle",
2
5
  "shell.search": "Rechercher",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Fermer la boîte de dialogue",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Effacer les données",
95
98
  "shell.eraseBrowserDataDescription": "Cette action supprime les paramètres Rigel, les clés API, la connexion GitHub et les brouillons non enregistrés de ce navigateur. Les fichiers enregistrés dans le dépôt ne seront pas modifiés. L’éditeur sera rechargé.",
96
99
  "shell.name": "Nom",
100
+ "shell.path": "Chemin",
97
101
  "shell.slug": "Identifiant d’URL",
98
102
  "shell.title": "Titre",
99
103
  "shell.category": "Catégorie",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Modifica l’URL, non l’identificatore del documento né il nome del file. La rotta del sito deve usare lo slug nei metadati.",
31
31
  "fields.newPageNotice": "Nuova pagina. Salva scrive il file JSON; la rotta appare quando il file è presente nel sito.",
32
32
  "fields.structure": "Struttura",
33
+ "fields.reorderSection": "Riordina {label}",
34
+ "fields.sectionReorderHint": "Trascina per riordinare o usa le frecce su e giù. Escape annulla il trascinamento. Le modifiche richiedono Salva.",
35
+ "fields.sectionMoved": "{label} spostato alla posizione {position} di {count}.",
33
36
  "fields.moveSectionUp": "Sposta in alto {label}",
34
37
  "fields.moveSectionDown": "Sposta in basso {label}",
35
38
  "fields.duplicateSection": "Duplica {label}",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Importazione della foto non riuscita ({status})",
163
163
  "media.invalidImportedUrl": "URL della foto importata non valido",
164
164
  "media.providerVideo": "Video {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Scegli file",
167
+ "media.removeFile": "Rimuovi file",
168
+ "media.noFile": "Nessun file",
169
+ "media.useFile": "Usa file",
170
+ "media.fileSources": "Origini dei file",
171
+ "media.uploadingFile": "Caricamento del file…",
172
+ "media.chooseAcceptedFile": "Scegli un file supportato corrispondente a: {accept}.",
173
+ "media.uploadFailedGeneric": "Caricamento del file non riuscito"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Principali",
3
+ "shell.template": "Modello",
4
+ "shell.chooseTemplate": "Scegli un modello",
2
5
  "shell.search": "Cerca",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Chiudi finestra",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Cancella dati",
95
98
  "shell.eraseBrowserDataDescription": "Verranno rimossi da questo browser le impostazioni di Rigel, le chiavi API, l’accesso a GitHub e le bozze non salvate. I file salvati nel repository non verranno modificati. L’editor verrà ricaricato.",
96
99
  "shell.name": "Nome",
100
+ "shell.path": "Percorso",
97
101
  "shell.slug": "Slug",
98
102
  "shell.title": "Titolo",
99
103
  "shell.category": "Categoria",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "ドキュメントの識別子やファイル名ではなく、URLを変更します。ウェブサイトのルートではslugメタデータを使う必要があります。",
31
31
  "fields.newPageNotice": "新しいページです。保存するとJSONファイルが書き込まれ、サイトにファイルが追加されるとルートが利用可能になります。",
32
32
  "fields.structure": "構造",
33
+ "fields.reorderSection": "{label}を並べ替え",
34
+ "fields.sectionReorderHint": "ドラッグするか上下の矢印キーで並べ替えます。Escapeでドラッグをキャンセルします。変更には保存が必要です。",
35
+ "fields.sectionMoved": "{label}を{count}件中{position}番目に移動しました。",
33
36
  "fields.moveSectionUp": "{label}を上へ移動",
34
37
  "fields.moveSectionDown": "{label}を下へ移動",
35
38
  "fields.duplicateSection": "{label}を複製",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "写真のインポートに失敗しました({status})",
163
163
  "media.invalidImportedUrl": "インポートした写真のURLが無効です",
164
164
  "media.providerVideo": "{provider}の動画",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "ファイルを選択",
167
+ "media.removeFile": "ファイルを解除",
168
+ "media.noFile": "ファイルなし",
169
+ "media.useFile": "ファイルを使用",
170
+ "media.fileSources": "ファイルのソース",
171
+ "media.uploadingFile": "ファイルをアップロード中…",
172
+ "media.chooseAcceptedFile": "対応する形式のファイルを選択してください:{accept}。",
173
+ "media.uploadFailedGeneric": "ファイルのアップロードに失敗しました"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "基本",
3
+ "shell.template": "テンプレート",
4
+ "shell.chooseTemplate": "テンプレートを選択",
2
5
  "shell.search": "検索",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "ダイアログを閉じる",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "データを消去",
95
98
  "shell.eraseBrowserDataDescription": "このブラウザーからRigelの設定、APIキー、GitHubのログイン情報、未保存の下書きを削除します。リポジトリに保存済みのファイルは変更されません。エディターが再読み込みされます。",
96
99
  "shell.name": "名前",
100
+ "shell.path": "パス",
97
101
  "shell.slug": "スラッグ",
98
102
  "shell.title": "タイトル",
99
103
  "shell.category": "カテゴリー",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Wijzigt de URL, niet de document-ID of bestandsnaam. De websiteroute moet de slugmetadata gebruiken.",
31
31
  "fields.newPageNotice": "Nieuwe pagina. Opslaan schrijft het JSON-bestand weg. De route is beschikbaar zodra de website het bestand heeft.",
32
32
  "fields.structure": "Structuur",
33
+ "fields.reorderSection": "{label} herschikken",
34
+ "fields.sectionReorderHint": "Sleep om te herschikken of gebruik de pijltjestoetsen omhoog en omlaag. Escape annuleert het slepen. Wijzigingen moeten worden opgeslagen.",
35
+ "fields.sectionMoved": "{label} verplaatst naar positie {position} van {count}.",
33
36
  "fields.moveSectionUp": "{label} omhoog verplaatsen",
34
37
  "fields.moveSectionDown": "{label} omlaag verplaatsen",
35
38
  "fields.duplicateSection": "{label} dupliceren",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Foto importeren mislukt ({status})",
163
163
  "media.invalidImportedUrl": "Ongeldige URL van de geïmporteerde foto",
164
164
  "media.providerVideo": "Video van {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Bestand kiezen",
167
+ "media.removeFile": "Bestand verwijderen",
168
+ "media.noFile": "Geen bestand",
169
+ "media.useFile": "Bestand gebruiken",
170
+ "media.fileSources": "Bestandsbronnen",
171
+ "media.uploadingFile": "Bestand uploaden…",
172
+ "media.chooseAcceptedFile": "Kies een ondersteund bestand dat overeenkomt met: {accept}.",
173
+ "media.uploadFailedGeneric": "Uploaden mislukt"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Standaard",
3
+ "shell.template": "Sjabloon",
4
+ "shell.chooseTemplate": "Kies een sjabloon",
2
5
  "shell.search": "Zoeken",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Dialoogvenster sluiten",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Gegevens wissen",
95
98
  "shell.eraseBrowserDataDescription": "Hiermee verwijder je Rigel-instellingen, API-sleutels, de GitHub-aanmelding en niet-opgeslagen concepten uit deze browser. Opgeslagen repositorybestanden blijven ongewijzigd. De editor wordt opnieuw geladen.",
96
99
  "shell.name": "Naam",
100
+ "shell.path": "Pad",
97
101
  "shell.slug": "Slug",
98
102
  "shell.title": "Titel",
99
103
  "shell.category": "Categorie",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "Меняет URL, но не идентификатор документа или имя файла. Маршрут сайта должен использовать метаданные slug.",
31
31
  "fields.newPageNotice": "Новая страница. Кнопка «Сохранить» записывает JSON-файл; маршрут появится, когда файл будет доступен сайту.",
32
32
  "fields.structure": "Структура",
33
+ "fields.reorderSection": "Изменить порядок: {label}",
34
+ "fields.sectionReorderHint": "Перетащите для изменения порядка или используйте стрелки вверх и вниз. Escape отменяет перетаскивание. Изменения требуют сохранения.",
35
+ "fields.sectionMoved": "{label}: перемещено на позицию {position} из {count}.",
33
36
  "fields.moveSectionUp": "Переместить вверх: {label}",
34
37
  "fields.moveSectionDown": "Переместить вниз: {label}",
35
38
  "fields.duplicateSection": "Дублировать: {label}",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "Не удалось импортировать фотографию ({status})",
163
163
  "media.invalidImportedUrl": "Некорректный URL импортированной фотографии",
164
164
  "media.providerVideo": "Видео из сервиса {provider}",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "Выбрать файл",
167
+ "media.removeFile": "Убрать файл",
168
+ "media.noFile": "Нет файла",
169
+ "media.useFile": "Использовать файл",
170
+ "media.fileSources": "Источники файлов",
171
+ "media.uploadingFile": "Загрузка файла…",
172
+ "media.chooseAcceptedFile": "Выберите поддерживаемый файл формата: {accept}.",
173
+ "media.uploadFailedGeneric": "Не удалось загрузить файл"
166
174
  }
@@ -1,4 +1,7 @@
1
1
  {
2
+ "shell.coreCategory": "Основные",
3
+ "shell.template": "Шаблон",
4
+ "shell.chooseTemplate": "Выберите шаблон",
2
5
  "shell.search": "Поиск",
3
6
  "shell.searchShortcut": "⌘/Ctrl + K",
4
7
  "shell.closeDialog": "Закрыть диалог",
@@ -94,6 +97,7 @@
94
97
  "shell.eraseData": "Удалить данные",
95
98
  "shell.eraseBrowserDataDescription": "Из этого браузера будут удалены настройки Rigel, ключи API, данные входа в GitHub и несохранённые черновики. Сохранённые файлы репозитория не изменятся. Редактор перезагрузится.",
96
99
  "shell.name": "Название",
100
+ "shell.path": "Путь",
97
101
  "shell.slug": "Слаг",
98
102
  "shell.title": "Заголовок",
99
103
  "shell.category": "Категория",
@@ -30,6 +30,9 @@
30
30
  "fields.documentSlugHint": "仅更改 URL,不会更改文档标识符或文件名。网站路由须使用 slug 元数据。",
31
31
  "fields.newPageNotice": "新页面。保存会写入 JSON 文件;网站获取该文件后,路由才会生效。",
32
32
  "fields.structure": "结构",
33
+ "fields.reorderSection": "重新排列{label}",
34
+ "fields.sectionReorderHint": "拖动或使用上下方向键重新排列。Escape 可取消拖动。更改需要保存。",
35
+ "fields.sectionMoved": "已将{label}移至第{position}项,共{count}项。",
33
36
  "fields.moveSectionUp": "上移{label}",
34
37
  "fields.moveSectionDown": "下移{label}",
35
38
  "fields.duplicateSection": "复制{label}",
@@ -162,5 +162,13 @@
162
162
  "media.photoImportFailedStatus": "照片导入失败({status})",
163
163
  "media.invalidImportedUrl": "导入照片的 URL 无效",
164
164
  "media.providerVideo": "{provider} 视频",
165
- "media.embedCaption": "{provider} · {title}"
165
+ "media.embedCaption": "{provider} · {title}",
166
+ "media.chooseFile": "选择文件",
167
+ "media.removeFile": "移除文件",
168
+ "media.noFile": "无文件",
169
+ "media.useFile": "使用文件",
170
+ "media.fileSources": "文件来源",
171
+ "media.uploadingFile": "正在上传文件…",
172
+ "media.chooseAcceptedFile": "请选择符合以下格式的受支持文件:{accept}。",
173
+ "media.uploadFailedGeneric": "文件上传失败"
166
174
  }