@hydraharness/harness-client-locale 0.1.1-rc.6

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/lib/index.js ADDED
@@ -0,0 +1,26 @@
1
+ import { settingsNamespace } from "@hydraharness/harness-settings";
2
+ import z from "@hydraharness/schemastery";
3
+ //#region lib/types/locale-settings.js
4
+ /** Locale preference stored in the Host user-settings document. */
5
+ /** Settings namespace owned by the locale plugin. */
6
+ const LOCALE_SETTINGS_NAMESPACE = "locale";
7
+ /** Field carrying an explicit locale selection; absence delegates to the browser. */
8
+ const LOCALE_PREFERENCE_FIELD = "preference";
9
+ /** Locale identifiers shipped by the browser client. */
10
+ const LOCALE_IDS = ["en"];
11
+ /** Durable locale schema; also the wire envelope the browser scope validates against. */
12
+ const LocaleSettingsSchema = z.object({ [LOCALE_PREFERENCE_FIELD]: z.union([...LOCALE_IDS]).required(false) });
13
+ //#endregion
14
+ //#region lib/types/index.js
15
+ /** Host registration for the browser locale preference. */
16
+ /**
17
+ * Register the durable locale section when a settings provider exists.
18
+ * @param ctx - Host context whose optional settings service owns the section.
19
+ */
20
+ function apply(ctx) {
21
+ ctx.inject(["settings"], (settingsCtx) => {
22
+ settingsCtx.settings.register(settingsNamespace(LOCALE_SETTINGS_NAMESPACE), LocaleSettingsSchema);
23
+ });
24
+ }
25
+ //#endregion
26
+ export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, apply };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@hydraharness/harness-client-locale`.
4
+ * @module @hydraharness/harness-client-locale/invariant
5
+ */
6
+ const PACKAGE_NAME = "@hydraharness/harness-client-locale";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-locale-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: ns-by-locale dictionary registry with a stable
13
+ * bind(ns) API — it emits no cordis events and owns no cross-plugin
14
+ * mutable relation; fallback-chain resolution and locale-store behavior are
15
+ * asserted directly by this package's behavior specs.
16
+ */
17
+ const install = () => {};
18
+ /**
19
+ * Register this package's invariant companion.
20
+ * @param ctx - Cordis context carrying the invariant service.
21
+ * @returns the installed registration's disposer after setup succeeds.
22
+ */
23
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
+ //#endregion
25
+ export { apply, inject, name };
@@ -0,0 +1,16 @@
1
+ import type { PropsLocale, PropsRuntime, PropsStore } from '@hydraharness/harness-client-ui-slots';
2
+ import type { createLanguageRowStore } from './settings-store.ts';
3
+ /** Injected business face: the preference write (t rides the standard locale seat). */
4
+ export interface LanguageRowInjected {
5
+ /** Switch the active locale (a registered locale id). */
6
+ setLocale: (id: string) => void;
7
+ }
8
+ /** Full component props: runtime share + store share + locale seat + injected face. */
9
+ export type LanguageRowComponentProps = PropsRuntime<'settings.general.item'> & PropsStore<ReturnType<typeof createLanguageRowStore>> & PropsLocale<'settings.locale'> & LanguageRowInjected;
10
+ /**
11
+ * Render the Language row.
12
+ * @param props - composed slot props.
13
+ * @returns the row element tree.
14
+ */
15
+ export declare function LanguageRow({ t, setLocale, useStore }: LanguageRowComponentProps): import("react").JSX.Element;
16
+ //# sourceMappingURL=LanguageRow.d.ts.map
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Browser-side locale registry. Bound translation functions retain stable
3
+ * identity for injected consumers. The plugin also registers the Language
4
+ * preference row into the settings General section — the locale feature owns
5
+ * its own settings surface.
6
+ */
7
+ import type { Context } from '@hydraharness/cordis';
8
+ import { type LocaleDictOf, type LocaleNamespaceMap, type Translate, type TranslateNS } from '@hydraharness/harness-client-ui-slots';
9
+ import type { ClientContext, SettingsScope } from '@hydraharness/harness-client-runtime/client';
10
+ import { type LocaleId, type LocaleSettings } from '../locale-settings.ts';
11
+ import { type CommonKey } from '../locales/index.ts';
12
+ import { type SettingsLocaleKey } from '../locales/settings.ts';
13
+ export type { LanguageRowComponentProps, LanguageRowInjected } from './LanguageRow.tsx';
14
+ export type { LanguageOptionRow, LanguageRowState } from './settings-store.ts';
15
+ export type { CommonKey } from '../locales/index.ts';
16
+ export type { LocaleId, LocaleSettings } from '../locale-settings.ts';
17
+ export type { Translate, TranslateNS } from '@hydraharness/harness-client-ui-slots';
18
+ declare module '@hydraharness/harness-client-ui-slots' {
19
+ interface LocaleNamespaceMap {
20
+ /** Shared cross-feature vocabulary, consulted by the lookup chain after the entry's own namespace misses. */
21
+ common: CommonKey;
22
+ /** This feature's own settings-row copy (the Language row). */
23
+ 'settings.locale': SettingsLocaleKey;
24
+ }
25
+ }
26
+ /** Locale dictionary: flat key to template string ({name} placeholders). */
27
+ export type LocaleDict = Record<string, string>;
28
+ /** One selectable locale: id plus its self-described display name. */
29
+ export interface LocaleDefinition {
30
+ /** Locale id (persisted; the setLocale argument). */
31
+ id: LocaleId;
32
+ /** Display name in its own language (English). */
33
+ label: string;
34
+ }
35
+ /** Immutable locale state published on every change. */
36
+ export interface LocaleSnapshot {
37
+ /** Active locale id. */
38
+ active: LocaleId;
39
+ /** Selectable locales in display order. */
40
+ locales: readonly LocaleDefinition[];
41
+ /** Monotonic change counter (registry or active changes). */
42
+ revision: number;
43
+ }
44
+ declare module '@hydraharness/cordis' {
45
+ interface Context {
46
+ locale: LocaleRuntime;
47
+ }
48
+ interface Events {
49
+ /**
50
+ * The active locale switched. Dictionary registrations do NOT emit this
51
+ * event (listeners may re-register slots in response, and boot registers
52
+ * one namespace per package); continuous render refresh rides the
53
+ * LocaleFace revision instead.
54
+ * @param snapshot - Current immutable locale snapshot.
55
+ * @mode emit
56
+ */
57
+ 'locale/change'(snapshot: LocaleSnapshot): void;
58
+ }
59
+ }
60
+ /**
61
+ * English is both the locale the UI opens in when the browser names no shipped
62
+ * language (and for non-browser runs), and the dictionary consulted after the
63
+ * active locale misses a key. It is currently the only shipped locale, so
64
+ * neither direction can leave a key unresolved.
65
+ */
66
+ export declare const FALLBACK_LOCALE: LocaleId;
67
+ /** Shared namespace for shell-level texts. */
68
+ export declare const COMMON_NS = "common";
69
+ /** Namespace owning this feature's settings-row copy. */
70
+ export declare const SETTINGS_NS = "settings.locale";
71
+ /**
72
+ * Dictionary registry plus locale preference. Lookup chain per key: the
73
+ * entry's namespace in the active locale -> that namespace's en fallback ->
74
+ * the shared common namespace (active, then en) -> the key itself (missing
75
+ * text stays visible, fail loud in the UI rather than blank). Reads go
76
+ * through {@link getLocale}; writes only through {@link setLocale};
77
+ * continuous sync through the `locale/change` event, or through the
78
+ * LocaleFace getSnapshot/subscribe pair the render machinery consumes
79
+ * (installed via `ctx.slots.installLocale`).
80
+ */
81
+ export declare class LocaleRuntime {
82
+ private dicts;
83
+ private bound;
84
+ private snapshot;
85
+ private listeners;
86
+ private readonly ctx;
87
+ private readonly host;
88
+ /** Browser-derived locale standing wherever no explicit Host selection does. */
89
+ private readonly provisional;
90
+ /**
91
+ * @param ctx - owning context (change events are emitted on it; the scope
92
+ * listener is released through ctx.effect on dispose).
93
+ * @param host - durable preference scope owned by the providing plugin;
94
+ * absent compositions (standalone dictionary registries) stay process-local.
95
+ */
96
+ constructor(ctx: Context, host?: SettingsScope<LocaleSettings>);
97
+ /**
98
+ * Read the current immutable locale snapshot.
99
+ * @returns the current snapshot (stable reference until the next change).
100
+ */
101
+ getLocale(): LocaleSnapshot;
102
+ /**
103
+ * LocaleFace getSnapshot: the current snapshot (carries `revision`; stable
104
+ * reference between changes, uSES-safe).
105
+ * @returns the current snapshot.
106
+ */
107
+ getSnapshot(): LocaleSnapshot;
108
+ /**
109
+ * LocaleFace subscribe: notified on every snapshot change (locale switch
110
+ * or dictionary registration — registrations bump the revision so already
111
+ * rendered outlets pick up late-arriving dictionaries).
112
+ * @param fn - change callback.
113
+ * @returns unsubscribe.
114
+ */
115
+ subscribe(fn: () => void): () => void;
116
+ /**
117
+ * Switch the active locale — the only user preference write entry.
118
+ *
119
+ * The durable write happens even when the id already matches the active
120
+ * locale, because the active value may be a provisional browser-derived or
121
+ * fallback resolution that nothing has stored yet. Picking the language
122
+ * already on screen is still an explicit choice, and it must survive a
123
+ * different browser sharing the same Hydra home. Only the render notification
124
+ * is conditional: republishing an unchanged locale would churn every
125
+ * subscriber for nothing.
126
+ * @param id - a registered locale id; unknown ids throw.
127
+ */
128
+ setLocale(id: string): void;
129
+ /**
130
+ * Adopt the scope's accepted durable selection without writing it back; an
131
+ * absent selection returns to the browser-derived locale.
132
+ * @param host - the constructor-narrowed scope driving this adoption.
133
+ */
134
+ private adopt;
135
+ /**
136
+ * Register a declared namespace's dictionaries, all locales in one call —
137
+ * the typed form: each dictionary is checked against the namespace's
138
+ * {@link LocaleNamespaceMap} key union (a missing or extra key is a
139
+ * compile error), and every shipped locale is required at registration.
140
+ * Duplicate (ns, locale) throws (single occupant; a
141
+ * namespace's texts have one owner). Registration bumps the revision so
142
+ * mounted outlets pick up late-arriving dictionaries.
143
+ * @param ns - a namespace merged into LocaleNamespaceMap.
144
+ * @param dicts - complete dictionaries keyed by locale id.
145
+ * @returns disposer removing every locale registered by this call (idempotent).
146
+ */
147
+ register<N extends keyof LocaleNamespaceMap & string>(ns: N, dicts: Record<LocaleId, LocaleDictOf<N>>): () => void;
148
+ /**
149
+ * Single-locale untyped form for namespaces outside the merge table
150
+ * (dynamic composition, tests).
151
+ * @param ns - namespace.
152
+ * @param locale - locale tag.
153
+ * @param dict - dictionary.
154
+ * @returns disposer (idempotent).
155
+ */
156
+ register(ns: string, locale: string, dict: LocaleDict): () => void;
157
+ /**
158
+ * Bind a declared namespace to a translate function typed to its
159
+ * dictionary key union (plus the shared common vocabulary) — the same key
160
+ * domain the framework-injected `t` seat carries. The returned reference
161
+ * is stable per namespace (repeat binds return the same function), so it
162
+ * can ride inject surfaces without breaking memoization.
163
+ * @param ns - a namespace merged into LocaleNamespaceMap.
164
+ * @returns the typed translate function (reads the active locale at call time).
165
+ */
166
+ bind<N extends keyof LocaleNamespaceMap & string>(ns: N): TranslateNS<N>;
167
+ /**
168
+ * Untyped form for namespaces outside the merge table (dynamic
169
+ * composition, tests).
170
+ * @param ns - namespace.
171
+ * @returns the translate function.
172
+ */
173
+ bind(ns: string): Translate;
174
+ private translate;
175
+ private lookup;
176
+ /**
177
+ * Advance the snapshot revision and notify LocaleFace subscribers (render
178
+ * refresh). Only an active-locale switch additionally emits
179
+ * `locale/change` — dictionary registrations stay off the event so
180
+ * registration-heavy boot cannot storm event listeners (which may
181
+ * re-register slots in response).
182
+ */
183
+ private publish;
184
+ }
185
+ /** Required services: slot registration plus the settings transport. */
186
+ export declare const inject: string[];
187
+ /**
188
+ * Client plugin body: provide the locale service with base dictionaries and
189
+ * register the feature-owned Language preference row into the General
190
+ * section's item slot (a feature owns its settings surface).
191
+ * @param ctx - client cordis context.
192
+ */
193
+ export declare function apply(ctx: ClientContext): void;
194
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Language row slot store: a mirror of the locale service snapshot. The
3
+ * plugin's apply-world change listener is the only writer; the row component
4
+ * reads via props.useStore.
5
+ */
6
+ import { type EngineStoreHandle } from '@hydraharness/harness-client-runtime/client';
7
+ /** One selectable locale row (id + self-described label). */
8
+ export interface LanguageOptionRow {
9
+ /** Locale id (the setLocale argument). */
10
+ id: string;
11
+ /** Display name in its own language (English). */
12
+ label: string;
13
+ }
14
+ /** Store state mirrored from the locale snapshot. */
15
+ export interface LanguageRowState {
16
+ /** Active locale id. */
17
+ active: string;
18
+ /** Selectable locales in display order. */
19
+ options: LanguageOptionRow[];
20
+ /** Service revision; -1 until first sync so revision 0 lands as a change. */
21
+ revision: number;
22
+ }
23
+ /** Declared action shape giving the exported factory a stable return type. */
24
+ type LanguageRowActions = {
25
+ sync: (draft: LanguageRowState, active: string, options: LanguageOptionRow[], revision: number) => void;
26
+ };
27
+ /**
28
+ * Declares the Language row state and write surface.
29
+ * @returns the store handle.
30
+ */
31
+ export declare function createLanguageRowStore(): EngineStoreHandle<LanguageRowState, LanguageRowActions>;
32
+ export {};
33
+ //# sourceMappingURL=settings-store.d.ts.map
@@ -0,0 +1,9 @@
1
+ /** Host registration for the browser locale preference. */
2
+ import type { Context } from '@hydraharness/cordis';
3
+ export { LOCALE_IDS, LOCALE_PREFERENCE_FIELD, LOCALE_SETTINGS_NAMESPACE, type LocaleId, type LocaleSettings, } from './locale-settings.ts';
4
+ /**
5
+ * Register the durable locale section when a settings provider exists.
6
+ * @param ctx - Host context whose optional settings service owns the section.
7
+ */
8
+ export declare function apply(ctx: Context): void;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@hydraharness/harness-client-locale`.
3
+ * @module @hydraharness/harness-client-locale/invariant
4
+ */
5
+ import type { Context } from '@hydraharness/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-locale-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
@@ -0,0 +1,18 @@
1
+ /** Locale preference stored in the Host user-settings document. */
2
+ import z from '@hydraharness/schemastery';
3
+ /** Settings namespace owned by the locale plugin. */
4
+ export declare const LOCALE_SETTINGS_NAMESPACE = "locale";
5
+ /** Field carrying an explicit locale selection; absence delegates to the browser. */
6
+ export declare const LOCALE_PREFERENCE_FIELD = "preference";
7
+ /** Locale identifiers shipped by the browser client. */
8
+ export declare const LOCALE_IDS: readonly ["en"];
9
+ /** Shipped locale identifier. */
10
+ export type LocaleId = typeof LOCALE_IDS[number];
11
+ /** Durable locale section shared by the Host schema and the browser scope. */
12
+ export interface LocaleSettings {
13
+ /** Explicit locale selection; absence delegates to the browser. */
14
+ preference?: LocaleId;
15
+ }
16
+ /** Durable locale schema; also the wire envelope the browser scope validates against. */
17
+ export declare const LocaleSettingsSchema: z<LocaleSettings>;
18
+ //# sourceMappingURL=locale-settings.d.ts.map
@@ -0,0 +1,30 @@
1
+ /** en base dictionary for the common namespace (the key-set source of truth). */
2
+ export declare const en: {
3
+ ok: string;
4
+ cancel: string;
5
+ close: string;
6
+ copy: string;
7
+ copied: string;
8
+ retry: string;
9
+ loading: string;
10
+ 'load.failed': string;
11
+ submit: string;
12
+ submitting: string;
13
+ next: string;
14
+ previous: string;
15
+ skip: string;
16
+ delete: string;
17
+ edit: string;
18
+ save: string;
19
+ search: string;
20
+ more: string;
21
+ collapse: string;
22
+ expand: string;
23
+ back: string;
24
+ unknown: string;
25
+ none: string;
26
+ truncated: string;
27
+ };
28
+ /** The common-namespace key union. */
29
+ export type CommonKey = keyof typeof en;
30
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** The common-namespace dictionary: shared cross-feature vocabulary. */
2
+ export { en } from './en.ts';
3
+ export type { CommonKey } from './en.ts';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,8 @@
1
+ /** `settings.locale` namespace dictionaries (the Language row's copy). */
2
+ /** English dictionary (the key-set source of truth). */
3
+ export declare const en: {
4
+ 'language.title': string;
5
+ };
6
+ /** The settings.locale namespace key union. */
7
+ export type SettingsLocaleKey = keyof typeof en;
8
+ //# sourceMappingURL=settings.d.ts.map
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@hydraharness/harness-client-locale",
3
+ "description": "Locale plugin: Host-backed en preference, browser-derived fallback, locale snapshots, and typed namespace dictionaries",
4
+ "version": "0.1.1-rc.6",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/MaiHongPhong1902/Hydra-Harness.git",
11
+ "directory": "packages/client/locale"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./src/*": "./src/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "hydra": {
33
+ "plugin": {
34
+ "application": "Apply the saved interface language and supply translations to interface plugins."
35
+ },
36
+ "client": {
37
+ "inject": [
38
+ "@hydraharness/harness-client-connection",
39
+ "@hydraharness/harness-client-runtime",
40
+ "@hydraharness/harness-client-ui-settings",
41
+ "@hydraharness/harness-api-remotes"
42
+ ],
43
+ "platform": "web",
44
+ "immediately": true
45
+ }
46
+ },
47
+ "license": "MIT",
48
+ "peerDependencies": {
49
+ "@hydraharness/cordis": "^4.0.2",
50
+ "@hydraharness/harness-api-remotes": "^0.1.1-rc.6",
51
+ "@hydraharness/harness-client-runtime": "^0.1.1-rc.6",
52
+ "@hydraharness/harness-client-ui-settings": "^0.1.1-rc.6",
53
+ "@hydraharness/harness-settings": "^0.1.1-rc.6",
54
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
55
+ "@hydraharness/harness-client-connection": "^0.1.1-rc.6"
56
+ },
57
+ "devDependencies": {
58
+ "@types/react": "~18.3.1",
59
+ "react": "^18.2.0",
60
+ "@hydraharness/cordis": "^4.0.2",
61
+ "@hydraharness/harness-api-remotes": "^0.1.1-rc.6",
62
+ "@hydraharness/harness-client-runtime": "^0.1.1-rc.6",
63
+ "@hydraharness/harness-client-test-runtime": "^0.1.1-rc.6",
64
+ "@hydraharness/harness-client-ui-primitives": "^0.1.1-rc.6",
65
+ "@hydraharness/harness-client-ui-slots": "^0.1.1-rc.6",
66
+ "@hydraharness/harness-client-ui-settings": "^0.1.1-rc.6",
67
+ "@hydraharness/harness-invariants": "^0.1.1-rc.6",
68
+ "@hydraharness/harness-client-connection": "^0.1.1-rc.6",
69
+ "@hydraharness/harness-settings": "^0.1.1-rc.6"
70
+ },
71
+ "dependencies": {
72
+ "@hydraharness/schemastery": "^3.18.2"
73
+ },
74
+ "files": [
75
+ "lib/index.js",
76
+ "lib/invariant.js",
77
+ "lib/client.js",
78
+ "lib/types/**/*.d.ts"
79
+ ],
80
+ "scripts": {
81
+ "bundle": "tsdown",
82
+ "watch": "tsdown --watch"
83
+ }
84
+ }