@coworkee/client 0.0.0-stage → 0.0.1024

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/dist/schema.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This file was auto-generated by openapi-typescript.
3
+ * Do not make direct changes to the file.
4
+ */
5
+ export {};
@@ -0,0 +1,7 @@
1
+ import { type CoworkeeClientOptions } from './client.ts';
2
+ /** Splits text that arrives in chunks into complete lines; the rest waits for the next chunk. */
3
+ export declare function lineSplitter(): (chunk: string, end?: boolean) => string[];
4
+ /** POSTs JSON and hands every line of the newline-delimited JSON answer (application/x-ndjson) to onEvent as it arrives. */
5
+ export declare function postNdjson<T>(path: string, body: unknown, onEvent: (event: T) => void, options?: CoworkeeClientOptions & {
6
+ signal?: AbortSignal;
7
+ }): Promise<void>;
package/dist/stream.js ADDED
@@ -0,0 +1,33 @@
1
+ import { csrfHeader } from "./client.js";
2
+ import { ApiError, problemOf } from "./errors.js";
3
+ /** Splits text that arrives in chunks into complete lines; the rest waits for the next chunk. */
4
+ export function lineSplitter() {
5
+ let rest = '';
6
+ return (chunk, end = false) => {
7
+ const lines = (rest + chunk).split('\n');
8
+ rest = end ? '' : lines.pop() ?? '';
9
+ return lines.map((line) => line.trim()).filter(Boolean);
10
+ };
11
+ }
12
+ /** POSTs JSON and hands every line of the newline-delimited JSON answer (application/x-ndjson) to onEvent as it arrives. */
13
+ export async function postNdjson(path, body, onEvent, options = {}) {
14
+ const headers = new Headers({ 'Content-Type': 'application/json', Accept: 'application/x-ndjson' });
15
+ if (options.token)
16
+ headers.set('Authorization', `Bearer ${await options.token()}`);
17
+ else
18
+ headers.set(csrfHeader.name, csrfHeader.value);
19
+ const response = await (options.fetch ?? fetch)(`${options.baseUrl ?? ''}${path}`, { method: 'POST', credentials: 'same-origin', headers, body: JSON.stringify(body), signal: options.signal });
20
+ if (!response.ok)
21
+ throw new ApiError(response.status, await problemOf(response));
22
+ const split = lineSplitter();
23
+ const decoder = new TextDecoder();
24
+ const reader = response.body?.getReader();
25
+ if (!reader)
26
+ return split(await response.text(), true).forEach((line) => onEvent(JSON.parse(line)));
27
+ for (;;) {
28
+ const { done, value } = await reader.read();
29
+ split(done ? decoder.decode() : decoder.decode(value, { stream: true }), done).forEach((line) => onEvent(JSON.parse(line)));
30
+ if (done)
31
+ return;
32
+ }
33
+ }
@@ -0,0 +1,11 @@
1
+ import { type CoworkeeClientOptions } from './client.ts';
2
+ /** English text → text in the user's language, as the API serves them (library texts, app texts, translations made in the app). */
3
+ export type Texts = Record<string, string>;
4
+ export type Translate = (key: string, ...args: unknown[]) => string;
5
+ export declare function getTexts(culture: string, options?: CoworkeeClientOptions): Promise<Texts>;
6
+ /** Fills {0}, {1}, ... like string.Format; format suffixes ({0:N0}) are ignored. */
7
+ export declare function format(text: string, args: readonly unknown[]): string;
8
+ /** Looks a key up, falls back to the key (the English text) and fills the placeholders. */
9
+ export declare const translator: (texts: Texts) => Translate;
10
+ /** The user's culture: the setting Localization.Culture, else the browser's first language, else English. */
11
+ export declare function pickCulture(setting?: string | null, browser?: readonly string[]): string;
package/dist/texts.js ADDED
@@ -0,0 +1,15 @@
1
+ import { apiRequest } from "./client.js";
2
+ export async function getTexts(culture, options) {
3
+ const result = await apiRequest(`/api/v1/localization/texts/${encodeURIComponent(culture)}`, {}, options);
4
+ return result?.texts ?? {};
5
+ }
6
+ /** Fills {0}, {1}, ... like string.Format; format suffixes ({0:N0}) are ignored. */
7
+ export function format(text, args) {
8
+ return args.length === 0 ? text : text.replace(/\{(\d+)(:[^}]*)?\}/g, (match, index) => index in args ? String(args[Number(index)]) : match);
9
+ }
10
+ /** Looks a key up, falls back to the key (the English text) and fills the placeholders. */
11
+ export const translator = (texts) => (key, ...args) => format(texts[key] || key, args);
12
+ /** The user's culture: the setting Localization.Culture, else the browser's first language, else English. */
13
+ export function pickCulture(setting, browser = typeof navigator === 'object' ? navigator.languages ?? [] : []) {
14
+ return setting?.trim() || browser.find(Boolean) || 'en';
15
+ }
@@ -0,0 +1,30 @@
1
+ import { type CoworkeeClientOptions } from './client.ts';
2
+ import type { components } from './schema.ts';
3
+ export type Theme = components['schemas']['ThemeDto'];
4
+ export type ColorMode = 'light' | 'dark';
5
+ /** The colors of a theme in one mode, MudBlazor's defaults filled in; keys in camel case (primary, textSecondary, ...). */
6
+ export declare function palette(theme: Theme | undefined, mode: ColorMode): Record<string, string>;
7
+ /** CSS custom properties of the theme: --cw-primary, --cw-surface, --cw-text, ..., --cw-font, --cw-heading-font, --cw-radius. */
8
+ export declare function cssVariables(theme: Theme | undefined, mode: ColorMode): Record<string, string>;
9
+ /** Light or dark: the setting Theme.Mode (light, dark, system) or the operating system's. */
10
+ export declare function resolveMode(setting?: string | null, prefersDark?: boolean): ColorMode;
11
+ /** An address for an img: SVG markup becomes a data URL, data URLs and addresses stay as they are. */
12
+ export declare function logoUrl(logo?: string | null): string | undefined;
13
+ export declare function applyTheme(theme: Theme | undefined, mode: ColorMode, root?: HTMLElement): void;
14
+ /** The theme of the signed-in user's organisation, or the default one (anonymous). */
15
+ export declare const getCurrentTheme: (options?: CoworkeeClientOptions) => Promise<{
16
+ id: string;
17
+ name: string;
18
+ isGlobal: boolean;
19
+ isDefault: boolean;
20
+ paletteLight: components["schemas"]["JsonElement"];
21
+ paletteDark: components["schemas"]["JsonElement"];
22
+ typography: null | components["schemas"]["JsonElement"];
23
+ layoutProperties: null | components["schemas"]["JsonElement"];
24
+ logo: null | string;
25
+ customCss: null | string;
26
+ revision: number | string;
27
+ shadows?: unknown;
28
+ options?: unknown;
29
+ isPublished: boolean;
30
+ }>;
package/dist/theme.js ADDED
@@ -0,0 +1,65 @@
1
+ import { apiRequest } from "./client.js";
2
+ /** MudBlazor's defaults, which a theme's palette overrides key by key (themes store only what they change). */
3
+ const defaults = {
4
+ light: {
5
+ primary: '#594ae2', primaryContrastText: '#ffffff', secondary: '#ff4081', secondaryContrastText: '#ffffff', background: '#ffffff', surface: '#ffffff',
6
+ textPrimary: '#424242', textSecondary: '#00000089', linesDefault: '#0000001e', appbarBackground: '#594ae2', appbarText: '#ffffff',
7
+ error: '#f44336', success: '#00c853', warning: '#ff9800', info: '#2196f3',
8
+ },
9
+ dark: {
10
+ primary: '#776be7', primaryContrastText: '#ffffff', secondary: '#ff4081', secondaryContrastText: '#ffffff', background: '#32333d', surface: '#373740',
11
+ textPrimary: '#ffffffb2', textSecondary: '#ffffff80', linesDefault: '#ffffff1e', appbarBackground: '#27272f', appbarText: '#ffffffb2',
12
+ error: '#f64e62', success: '#0bba83', warning: '#ffa800', info: '#3299ff',
13
+ },
14
+ };
15
+ const variables = [
16
+ ['primary', 'primary'], ['primary-text', 'primaryContrastText'], ['secondary', 'secondary'], ['secondary-text', 'secondaryContrastText'],
17
+ ['background', 'background'], ['surface', 'surface'], ['text', 'textPrimary'], ['text-secondary', 'textSecondary'], ['lines', 'linesDefault'],
18
+ ['appbar', 'appbarBackground'], ['appbar-text', 'appbarText'], ['error', 'error'], ['success', 'success'], ['warning', 'warning'], ['info', 'info'],
19
+ ];
20
+ const camel = (key) => key.charAt(0).toLowerCase() + key.slice(1);
21
+ const record = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
22
+ /** The colors of a theme in one mode, MudBlazor's defaults filled in; keys in camel case (primary, textSecondary, ...). */
23
+ export function palette(theme, mode) {
24
+ const stored = record(mode === 'dark' ? theme?.paletteDark : theme?.paletteLight);
25
+ return { ...defaults[mode], ...Object.fromEntries(Object.entries(stored).filter(([, value]) => typeof value === 'string').map(([key, value]) => [camel(key), value])) };
26
+ }
27
+ function fontOf(typography, name) {
28
+ const family = record(record(typography)[name]).FontFamily ?? record(record(typography)[camel(name)]).fontFamily;
29
+ return Array.isArray(family) && family.length > 0 ? family.map((font) => /[\s"]/.test(String(font)) ? `"${String(font).replace(/"/g, '')}"` : String(font)).join(', ') : undefined;
30
+ }
31
+ /** CSS custom properties of the theme: --cw-primary, --cw-surface, --cw-text, ..., --cw-font, --cw-heading-font, --cw-radius. */
32
+ export function cssVariables(theme, mode) {
33
+ const colors = palette(theme, mode);
34
+ const result = Object.fromEntries(variables.map(([name, key]) => [`--cw-${name}`, colors[key]]));
35
+ const font = fontOf(theme?.typography, 'Default');
36
+ if (font)
37
+ result['--cw-font'] = font;
38
+ const heading = fontOf(theme?.typography, 'H1') ?? font;
39
+ if (heading)
40
+ result['--cw-heading-font'] = heading;
41
+ const layout = record(theme?.layoutProperties);
42
+ const radius = layout.DefaultBorderRadius ?? layout.defaultBorderRadius;
43
+ if (typeof radius === 'string' && radius)
44
+ result['--cw-radius'] = radius;
45
+ return result;
46
+ }
47
+ /** Light or dark: the setting Theme.Mode (light, dark, system) or the operating system's. */
48
+ export function resolveMode(setting, prefersDark = typeof matchMedia === 'function' && matchMedia('(prefers-color-scheme: dark)').matches) {
49
+ return setting === 'dark' || setting === 'light' ? setting : prefersDark ? 'dark' : 'light';
50
+ }
51
+ /** An address for an img: SVG markup becomes a data URL, data URLs and addresses stay as they are. */
52
+ export function logoUrl(logo) {
53
+ const value = logo?.trim();
54
+ if (!value)
55
+ return undefined;
56
+ return value.startsWith('<') ? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(value)}` : value;
57
+ }
58
+ export function applyTheme(theme, mode, root = document.documentElement) {
59
+ for (const [name, value] of Object.entries(cssVariables(theme, mode)))
60
+ root.style.setProperty(name, value);
61
+ root.style.colorScheme = mode;
62
+ root.dataset.cwMode = mode;
63
+ }
64
+ /** The theme of the signed-in user's organisation, or the default one (anonymous). */
65
+ export const getCurrentTheme = (options) => apiRequest('/api/v1/themes/current', {}, options);