@vit-foundation/ui 0.3.0 → 0.7.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.
@@ -1,5 +1,7 @@
1
1
  <script lang="ts">
2
2
  import { getUiConfig } from '../../config/context.js';
3
+ import ActionLabel from '../../edit/ActionLabel.svelte';
4
+ import Editable from '../../edit/Editable.svelte';
3
5
  import { withNewsletterIntent } from '../../forms/transport.js';
4
6
  import type { NewsletterToggleFormInstance } from './AccountPanel.svelte';
5
7
  import Button from '../ui/Button.svelte';
@@ -27,28 +29,49 @@
27
29
 
28
30
  <section class="newsletter" aria-labelledby="newsletter-title">
29
31
  <div class="inner band">
30
- <h2 id="newsletter-title">{msg.newsletter_title()}</h2>
31
- <p>{msg.newsletter_intro()}</p>
32
+ <Editable edit={config.messageEdit?.('newsletter_title')} value={msg.newsletter_title()}>
33
+ {#snippet children(text, attrs)}<h2 id="newsletter-title" {...attrs}>{text}</h2>{/snippet}
34
+ </Editable>
35
+ <Editable edit={config.messageEdit?.('newsletter_intro')} value={msg.newsletter_intro()}>
36
+ {#snippet children(text, attrs)}<p {...attrs}>{text}</p>{/snippet}
37
+ </Editable>
32
38
 
33
39
  {#if account === null}
34
40
  <p class="prompt">
35
- {msg.newsletter_promptLoggedOut()}
41
+ <Editable
42
+ edit={config.messageEdit?.('newsletter_promptLoggedOut')}
43
+ value={msg.newsletter_promptLoggedOut()}
44
+ >
45
+ {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
46
+ </Editable>
36
47
  <Link href={withNewsletterIntent('/signup')}>{msg.comments_signupLink()}</Link>
37
48
  ·
38
49
  <Link href={withNewsletterIntent('/login')}>{msg.comments_loginLink()}</Link>
39
50
  </p>
40
51
  {:else if account.newsletterSubscribed}
41
52
  <p class="prompt">
42
- {msg.newsletter_subscribedNote()}
53
+ <Editable
54
+ edit={config.messageEdit?.('newsletter_subscribedNote')}
55
+ value={msg.newsletter_subscribedNote()}
56
+ >
57
+ {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
58
+ </Editable>
43
59
  <Link href="/account">{msg.account_navLabel()}</Link>
44
60
  </p>
45
61
  {:else}
46
62
  <form {...f}>
47
63
  <input {...f.fields.action.as('hidden', 'subscribe')} />
48
64
  <LocaleField field={f.fields.locale} />
49
- <Button type="submit" pending={f.pending}>
50
- {msg.account_newsletterSubscribe()}
51
- </Button>
65
+ <ActionLabel
66
+ edit={config.messageEdit?.('account_newsletterSubscribe')}
67
+ value={msg.account_newsletterSubscribe()}
68
+ >
69
+ {#snippet control()}
70
+ <Button type="submit" pending={f.pending}>
71
+ {msg.account_newsletterSubscribe()}
72
+ </Button>
73
+ {/snippet}
74
+ </ActionLabel>
52
75
  </form>
53
76
  {#if f.result}
54
77
  <div class="feedback-slot">
@@ -19,6 +19,7 @@
19
19
 
20
20
  <script lang="ts">
21
21
  import { getUiConfig } from '../../config/context.js';
22
+ import ActionLabel from '../../edit/ActionLabel.svelte';
22
23
  import { CONTACT_MESSAGE, CONTACT_NAME, EMAIL } from '../../forms/constraints.js';
23
24
  import { CONTACT_CATEGORIES } from '../../content/types.js';
24
25
  import { contactCategoryLabel } from '../../utils/contact.js';
@@ -120,7 +121,11 @@
120
121
  <Honeypot form={f.fields} id="contact-website" />
121
122
 
122
123
  <div class="actions">
123
- <Button type="submit" pending={f.pending}>{msg.contact_submit()}</Button>
124
+ <ActionLabel edit={config.messageEdit?.('contact_submit')} value={msg.contact_submit()}>
125
+ {#snippet control()}
126
+ <Button type="submit" pending={f.pending}>{msg.contact_submit()}</Button>
127
+ {/snippet}
128
+ </ActionLabel>
124
129
  </div>
125
130
 
126
131
  {#if f.result && !f.result.ok}
@@ -30,8 +30,12 @@
30
30
  </script>
31
31
 
32
32
  {#if jobs.length === 0}
33
- <p class="empty">{msg.jobs_empty()}</p>
34
- <p class="nudge">{msg.jobs_newsletterNudge()}</p>
33
+ <Editable edit={config.messageEdit?.('jobs_empty')} value={msg.jobs_empty()}>
34
+ {#snippet children(text, attrs)}<p class="empty" {...attrs}>{text}</p>{/snippet}
35
+ </Editable>
36
+ <Editable edit={config.messageEdit?.('jobs_newsletterNudge')} value={msg.jobs_newsletterNudge()}>
37
+ {#snippet children(text, attrs)}<p class="nudge" {...attrs}>{text}</p>{/snippet}
38
+ </Editable>
35
39
  {:else}
36
40
  <ul>
37
41
  {#each jobs as job (job.slug)}
@@ -49,7 +53,9 @@
49
53
  </li>
50
54
  {/each}
51
55
  </ul>
52
- <p class="nudge">{msg.jobs_newsletterNudge()}</p>
56
+ <Editable edit={config.messageEdit?.('jobs_newsletterNudge')} value={msg.jobs_newsletterNudge()}>
57
+ {#snippet children(text, attrs)}<p class="nudge" {...attrs}>{text}</p>{/snippet}
58
+ </Editable>
53
59
  {/if}
54
60
 
55
61
  <style>
@@ -1,6 +1,9 @@
1
1
  <script lang="ts">
2
2
  import { getUiConfig } from '../../config/context.js';
3
3
  import type { SiteLink } from '../../config/types.js';
4
+ import ActionLabel from '../../edit/ActionLabel.svelte';
5
+ import Editable from '../../edit/Editable.svelte';
6
+ import type { EditDescriptor } from '../../edit/types.js';
4
7
  import Link from '../ui/Link.svelte';
5
8
 
6
9
  /**
@@ -10,9 +13,11 @@
10
13
  */
11
14
  interface Props {
12
15
  links: SiteLink[];
16
+ /** Edit descriptors for the link labels — see Nav. */
17
+ editFor?: (link: SiteLink) => EditDescriptor | undefined;
13
18
  }
14
19
 
15
- let { links }: Props = $props();
20
+ let { links, editFor = undefined }: Props = $props();
16
21
 
17
22
  const config = getUiConfig();
18
23
 
@@ -26,10 +31,22 @@
26
31
  </span>
27
32
  <ul>
28
33
  {#each links as link (link.href)}
29
- <li><Link href={link.href}>{link.label}</Link></li>
34
+ <li>
35
+ <ActionLabel edit={editFor?.(link)} value={link.label}>
36
+ {#snippet control()}<Link href={link.href}>{link.label}</Link>{/snippet}
37
+ </ActionLabel>
38
+ </li>
30
39
  {/each}
31
40
  </ul>
32
- <p class="rights">© {year} {config.messages.footer_rights()}</p>
41
+ <p class="rights">
42
+ © {year}
43
+ <Editable
44
+ edit={config.messageEdit?.('footer_rights')}
45
+ value={config.messages.footer_rights()}
46
+ >
47
+ {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
48
+ </Editable>
49
+ </p>
33
50
  </div>
34
51
  </footer>
35
52
 
@@ -1,4 +1,5 @@
1
1
  import type { SiteLink } from '../../config/types.js';
2
+ import type { EditDescriptor } from '../../edit/types.js';
2
3
  /**
3
4
  * The links are a prop rather than a package constant: the host app owns
4
5
  * its routes, and Nav and Footer render the same list — the foundation
@@ -6,6 +7,8 @@ import type { SiteLink } from '../../config/types.js';
6
7
  */
7
8
  interface Props {
8
9
  links: SiteLink[];
10
+ /** Edit descriptors for the link labels — see Nav. */
11
+ editFor?: (link: SiteLink) => EditDescriptor | undefined;
9
12
  }
10
13
  declare const Footer: import("svelte").Component<Props, {}, "">;
11
14
  type Footer = ReturnType<typeof Footer>;
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
2
  import { getUiConfig } from '../../config/context.js';
3
3
  import type { SiteLink } from '../../config/types.js';
4
+ import ActionLabel from '../../edit/ActionLabel.svelte';
5
+ import type { EditDescriptor } from '../../edit/types.js';
4
6
  import { isPathUnder } from '../../utils/paths.js';
5
7
  import GhostButton from '../ui/GhostButton.svelte';
6
8
  import Link from '../ui/Link.svelte';
@@ -16,9 +18,16 @@
16
18
  account?: { displayName: string } | null;
17
19
  /** The URL highlighting reads from. Only tests and stories pass one. */
18
20
  url?: URL;
21
+ /**
22
+ * Edit descriptors for the link LABELS — a function because the host
23
+ * owns both the routes and the message keys behind them. While the
24
+ * adapter is editing, a link with a descriptor renders as editable
25
+ * text instead of an anchor (see ActionLabel).
26
+ */
27
+ editFor?: (link: SiteLink) => EditDescriptor | undefined;
19
28
  }
20
29
 
21
- let { links, account = null, url = undefined }: Props = $props();
30
+ let { links, account = null, url = undefined, editFor = undefined }: Props = $props();
22
31
 
23
32
  const config = getUiConfig();
24
33
  const msg = $derived(config.messages);
@@ -77,9 +86,13 @@
77
86
  <ul class="links">
78
87
  {#each links as link (link.href)}
79
88
  <li>
80
- <Link href={link.href} aria-current={isCurrent(link.href) ? 'page' : undefined}>
81
- {link.label}
82
- </Link>
89
+ <ActionLabel edit={editFor?.(link)} value={link.label}>
90
+ {#snippet control()}
91
+ <Link href={link.href} aria-current={isCurrent(link.href) ? 'page' : undefined}>
92
+ {link.label}
93
+ </Link>
94
+ {/snippet}
95
+ </ActionLabel>
83
96
  </li>
84
97
  {/each}
85
98
  </ul>
@@ -1,4 +1,5 @@
1
1
  import type { SiteLink } from '../../config/types.js';
2
+ import type { EditDescriptor } from '../../edit/types.js';
2
3
  interface Props {
3
4
  /**
4
5
  * Primary navigation entries. A prop rather than a package constant:
@@ -12,6 +13,13 @@ interface Props {
12
13
  } | null;
13
14
  /** The URL highlighting reads from. Only tests and stories pass one. */
14
15
  url?: URL;
16
+ /**
17
+ * Edit descriptors for the link LABELS — a function because the host
18
+ * owns both the routes and the message keys behind them. While the
19
+ * adapter is editing, a link with a descriptor renders as editable
20
+ * text instead of an anchor (see ActionLabel).
21
+ */
22
+ editFor?: (link: SiteLink) => EditDescriptor | undefined;
15
23
  }
16
24
  declare const Nav: import("svelte").Component<Props, {}, "">;
17
25
  type Nav = ReturnType<typeof Nav>;
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import { getUiConfig } from '../../config/context.js';
3
+ import ActionLabel from '../../edit/ActionLabel.svelte';
3
4
  import Link from './Link.svelte';
4
5
 
5
6
  /**
@@ -37,21 +38,32 @@
37
38
 
38
39
  {#if pages > 1 || isOutOfRange}
39
40
  <nav class="pagination" aria-label={msg.pagination_label()}>
40
- {#if hasPrevious}
41
- <Link href={href(previousPage)} rel="prev">{msg.pagination_previous()}</Link>
42
- {:else}
43
- <span class="disabled">{msg.pagination_previous()}</span>
44
- {/if}
41
+ <ActionLabel
42
+ edit={config.messageEdit?.('pagination_previous')}
43
+ value={msg.pagination_previous()}
44
+ >
45
+ {#snippet control()}
46
+ {#if hasPrevious}
47
+ <Link href={href(previousPage)} rel="prev">{msg.pagination_previous()}</Link>
48
+ {:else}
49
+ <span class="disabled">{msg.pagination_previous()}</span>
50
+ {/if}
51
+ {/snippet}
52
+ </ActionLabel>
45
53
 
46
54
  <p class="status" aria-live="polite">
47
55
  {msg.pagination_status({ page, pages })}
48
56
  </p>
49
57
 
50
- {#if hasNext}
51
- <Link href={href(page + 1)} rel="next">{msg.pagination_next()}</Link>
52
- {:else}
53
- <span class="disabled">{msg.pagination_next()}</span>
54
- {/if}
58
+ <ActionLabel edit={config.messageEdit?.('pagination_next')} value={msg.pagination_next()}>
59
+ {#snippet control()}
60
+ {#if hasNext}
61
+ <Link href={href(page + 1)} rel="next">{msg.pagination_next()}</Link>
62
+ {:else}
63
+ <span class="disabled">{msg.pagination_next()}</span>
64
+ {/if}
65
+ {/snippet}
66
+ </ActionLabel>
55
67
  </nav>
56
68
  {/if}
57
69
 
@@ -10,6 +10,8 @@
10
10
 
11
11
  <script lang="ts">
12
12
  import { getUiConfig } from '../../config/context.js';
13
+ import ActionLabel from '../../edit/ActionLabel.svelte';
14
+ import Editable from '../../edit/Editable.svelte';
13
15
  import { COMMENT_BODY } from '../../forms/constraints.js';
14
16
  import type { CommentData, CommentThreadData } from '../../content/types.js';
15
17
  import Button from '../ui/Button.svelte';
@@ -67,10 +69,16 @@
67
69
  {/snippet}
68
70
 
69
71
  <section aria-labelledby="comments-heading">
70
- <h2 class="subsection-heading" id="comments-heading">{msg.comments_title()}</h2>
72
+ <Editable edit={config.messageEdit?.('comments_title')} value={msg.comments_title()}>
73
+ {#snippet children(text, attrs)}
74
+ <h2 class="subsection-heading" id="comments-heading" {...attrs}>{text}</h2>
75
+ {/snippet}
76
+ </Editable>
71
77
 
72
78
  {#if comments.length === 0}
73
- <p class="empty">{msg.comments_empty()}</p>
79
+ <Editable edit={config.messageEdit?.('comments_empty')} value={msg.comments_empty()}>
80
+ {#snippet children(text, attrs)}<p class="empty" {...attrs}>{text}</p>{/snippet}
81
+ </Editable>
74
82
  {:else}
75
83
  <ul class="list">
76
84
  {#each comments as thread (thread.id)}
@@ -111,9 +119,16 @@
111
119
  <input {...reply.fields.weeklySlug.as('hidden', weeklySlug)} />
112
120
  <input {...reply.fields.parentId.as('hidden', String(thread.id))} />
113
121
  <div class="actions">
114
- <Button type="submit" pending={reply.pending}>
115
- {msg.comments_replySubmit()}
116
- </Button>
122
+ <ActionLabel
123
+ edit={config.messageEdit?.('comments_replySubmit')}
124
+ value={msg.comments_replySubmit()}
125
+ >
126
+ {#snippet control()}
127
+ <Button type="submit" pending={reply.pending}>
128
+ {msg.comments_replySubmit()}
129
+ </Button>
130
+ {/snippet}
131
+ </ActionLabel>
117
132
  </div>
118
133
  <FormErrorFeedback result={reply.result} messages={errorMessages} />
119
134
  </form>
@@ -141,14 +156,23 @@
141
156
  <input {...main.fields.weeklySlug.as('hidden', weeklySlug)} />
142
157
 
143
158
  <div class="actions">
144
- <Button type="submit" pending={main.pending}>{msg.comments_submit()}</Button>
159
+ <ActionLabel edit={config.messageEdit?.('comments_submit')} value={msg.comments_submit()}>
160
+ {#snippet control()}
161
+ <Button type="submit" pending={main.pending}>{msg.comments_submit()}</Button>
162
+ {/snippet}
163
+ </ActionLabel>
145
164
  </div>
146
165
 
147
166
  <FormErrorFeedback result={main.result} messages={errorMessages} />
148
167
  </form>
149
168
  {:else}
150
169
  <p class="login-prompt">
151
- {msg.comments_loginPrompt()}
170
+ <Editable
171
+ edit={config.messageEdit?.('comments_loginPrompt')}
172
+ value={msg.comments_loginPrompt()}
173
+ >
174
+ {#snippet children(text, attrs)}<span {...attrs}>{text}</span>{/snippet}
175
+ </Editable>
152
176
  <Link href="/login">{msg.comments_loginLink()}</Link>
153
177
  ·
154
178
  <Link href="/signup">{msg.comments_signupLink()}</Link>
@@ -37,7 +37,10 @@ export function setUiConfig(input) {
37
37
  },
38
38
  get messages() {
39
39
  return input().messages ?? defaultMessages;
40
- }
40
+ },
41
+ // Undefined means inert: components render their message strings as
42
+ // plain text, which is every app but the CMS.
43
+ messageEdit: (key) => input().messageEdit?.(key)
41
44
  };
42
45
  return setContext(KEY, resolved);
43
46
  }
@@ -1,3 +1,4 @@
1
+ import type { EditDescriptor } from '../edit/types.js';
1
2
  /**
2
3
  * The site's locales and their canonical member. Fixed rather than generic:
3
4
  * `ca` is load-bearing — it is the required key of every localized column
@@ -156,6 +157,14 @@ export interface UiConfig {
156
157
  /** Appended to document titles and shown as the wordmark. */
157
158
  siteName: string;
158
159
  messages: UiMessages;
160
+ /**
161
+ * Edit descriptor for one of THIS config's message strings, by catalog
162
+ * key — the interface-wording half of edit mode. Optional and undefined by
163
+ * default, so a read-only app (and every story/test) renders the messages
164
+ * as plain text. A CMS-shaped host supplies it alongside an EditAdapter;
165
+ * components offer only their parameterless, plain-text message sites.
166
+ */
167
+ messageEdit?: (key: string) => EditDescriptor | undefined;
159
168
  }
160
169
  /** What an app hands UiProvider: any subset; the rest keeps package defaults. */
161
170
  export interface UiConfigInput {
@@ -166,4 +175,5 @@ export interface UiConfigInput {
166
175
  canonicalPathname?: UiConfig['canonicalPathname'];
167
176
  siteName?: string;
168
177
  messages?: UiMessages;
178
+ messageEdit?: UiConfig['messageEdit'];
169
179
  }
@@ -0,0 +1,45 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import { getEditAdapter } from './context.js';
4
+ import Editable from './Editable.svelte';
5
+ import type { EditDescriptor } from './types.js';
6
+
7
+ /**
8
+ * The label of an INTERACTIVE control — a button, a nav link — made
9
+ * editable the way WeeklieCard's title is: while the adapter is editing
10
+ * and a descriptor is supplied, the control is REPLACED by its label as
11
+ * editable text, because a caret inside a live control would activate it
12
+ * (a link navigates mid-draft, a label focuses its input). The control
13
+ * comes back the moment editing turns off; with no descriptor or no
14
+ * adapter it renders untouched, byte-identical to a read-only build.
15
+ */
16
+ interface Props {
17
+ /** Undefined leaves the control alone — the inert default. */
18
+ edit: EditDescriptor | undefined;
19
+ /** The label text the control renders. */
20
+ value: string;
21
+ /** The real control, rendered whenever the label is not being edited. */
22
+ control: Snippet;
23
+ }
24
+
25
+ let { edit, value, control }: Props = $props();
26
+
27
+ const adapter = getEditAdapter();
28
+ const editing = $derived(edit !== undefined && (adapter?.isEditing ?? false));
29
+ </script>
30
+
31
+ {#if editing}
32
+ <Editable {edit} {value}>
33
+ {#snippet children(text, attrs)}<span class="action-label" {...attrs}>{text}</span>{/snippet}
34
+ </Editable>
35
+ {:else}
36
+ {@render control()}
37
+ {/if}
38
+
39
+ <style>
40
+ /* Roughly the control's footprint, so the swap doesn't collapse the row. */
41
+ .action-label {
42
+ display: inline-block;
43
+ padding: 0.2em 0.4em;
44
+ }
45
+ </style>
@@ -0,0 +1,22 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { EditDescriptor } from './types.js';
3
+ /**
4
+ * The label of an INTERACTIVE control — a button, a nav link — made
5
+ * editable the way WeeklieCard's title is: while the adapter is editing
6
+ * and a descriptor is supplied, the control is REPLACED by its label as
7
+ * editable text, because a caret inside a live control would activate it
8
+ * (a link navigates mid-draft, a label focuses its input). The control
9
+ * comes back the moment editing turns off; with no descriptor or no
10
+ * adapter it renders untouched, byte-identical to a read-only build.
11
+ */
12
+ interface Props {
13
+ /** Undefined leaves the control alone — the inert default. */
14
+ edit: EditDescriptor | undefined;
15
+ /** The label text the control renders. */
16
+ value: string;
17
+ /** The real control, rendered whenever the label is not being edited. */
18
+ control: Snippet;
19
+ }
20
+ declare const ActionLabel: import("svelte").Component<Props, {}, "">;
21
+ type ActionLabel = ReturnType<typeof ActionLabel>;
22
+ export default ActionLabel;
@@ -1,5 +1,14 @@
1
1
  import type { Locale } from '../config/types.js';
2
2
  import type { EditableEntity, EditDescriptor } from './types.js';
3
+ /**
4
+ * Descriptor for one interface-wording message (a Paraglide catalog key).
5
+ * Only parameterless messages are sensibly editable in place — editing the
6
+ * RENDERED text of a parameterized one would overwrite its template.
7
+ */
8
+ export declare function chromeEdit(key: string, locale: Locale, options?: {
9
+ format?: EditDescriptor['format'];
10
+ label?: string;
11
+ }): EditDescriptor;
3
12
  /** Descriptor for one page-copy block (`page_content` row). */
4
13
  export declare function pageCopyEdit(page: string, sectionKey: string, locale: Locale, options?: {
5
14
  format?: EditDescriptor['format'];
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Descriptor for one interface-wording message (a Paraglide catalog key).
3
+ * Only parameterless messages are sensibly editable in place — editing the
4
+ * RENDERED text of a parameterized one would overwrite its template.
5
+ */
6
+ export function chromeEdit(key, locale, options) {
7
+ return { ref: { kind: 'chrome', key }, locale, ...options };
8
+ }
1
9
  /** Descriptor for one page-copy block (`page_content` row). */
2
10
  export function pageCopyEdit(page, sectionKey, locale, options) {
3
11
  return { ref: { kind: 'page-copy', page, sectionKey }, locale, ...options };
@@ -1,5 +1,6 @@
1
1
  export { getEditAdapter, setEditAdapter } from './context.js';
2
+ export { default as ActionLabel } from './ActionLabel.svelte';
2
3
  export { default as Editable } from './Editable.svelte';
3
- export { entityEdit, pageCopyEdit } from './helpers.js';
4
+ export { chromeEdit, entityEdit, pageCopyEdit } from './helpers.js';
4
5
  export { localize } from './types.js';
5
6
  export type { ContentRef, EditableEntity, EditAdapter, EditDescriptor, LocalizedText } from './types.js';
@@ -1,4 +1,5 @@
1
1
  export { getEditAdapter, setEditAdapter } from './context.js';
2
+ export { default as ActionLabel } from './ActionLabel.svelte';
2
3
  export { default as Editable } from './Editable.svelte';
3
- export { entityEdit, pageCopyEdit } from './helpers.js';
4
+ export { chromeEdit, entityEdit, pageCopyEdit } from './helpers.js';
4
5
  export { localize } from './types.js';
@@ -17,8 +17,12 @@ export declare function localize(value: LocalizedText, locale: Locale): string;
17
17
  */
18
18
  export type EditableEntity = 'weeklies' | 'projects' | 'milestones' | 'team_members' | 'job_openings';
19
19
  /**
20
- * The identity of one editable string in the content model: either a page
21
- * copy block (`page_content` row) or one localized field of a domain entity.
20
+ * The identity of one editable string in the content model: a page copy
21
+ * block (`page_content` row), one localized field of a domain entity, or one
22
+ * interface-wording message (`chrome`) — a Paraglide catalog key. What a
23
+ * chrome save MEANS is the host's decision, like every other ref: the
24
+ * foundation CMS writes the website's `ui_messages` table, from which the
25
+ * catalogs are regenerated at deploy.
22
26
  */
23
27
  export type ContentRef = {
24
28
  kind: 'page-copy';
@@ -29,6 +33,9 @@ export type ContentRef = {
29
33
  entity: EditableEntity;
30
34
  id: string | number;
31
35
  field: string;
36
+ } | {
37
+ kind: 'chrome';
38
+ key: string;
32
39
  };
33
40
  /** What a component hands the adapter alongside the new value. */
34
41
  export interface EditDescriptor {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vit-foundation/ui",
3
- "version": "0.3.0",
3
+ "version": "0.7.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",
@@ -122,7 +122,7 @@
122
122
  "svelte"
123
123
  ],
124
124
  "license": "MIT",
125
- "description": "Fundaci\u00f3 VIT's shared Svelte 5 component library with pluggable edit mode",
125
+ "description": "Fundació VIT's shared Svelte 5 component library with pluggable edit mode",
126
126
  "repository": {
127
127
  "type": "git",
128
128
  "url": "git+https://github.com/fndvit/VizVit-UI.git"