@abstractframework/ui-kit 0.1.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.
- package/README.md +51 -0
- package/dist/af_select.d.ts +30 -0
- package/dist/af_select.d.ts.map +1 -0
- package/dist/af_select.js +170 -0
- package/dist/icon.d.ts +8 -0
- package/dist/icon.d.ts.map +1 -0
- package/dist/icon.js +61 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/provider_model_select.d.ts +33 -0
- package/dist/provider_model_select.d.ts.map +1 -0
- package/dist/provider_model_select.js +57 -0
- package/dist/theme.d.ts +14 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +45 -0
- package/dist/theme_select.d.ts +16 -0
- package/dist/theme_select.d.ts.map +1 -0
- package/dist/theme_select.js +35 -0
- package/dist/typography.d.ts +19 -0
- package/dist/typography.d.ts.map +1 -0
- package/dist/typography.js +31 -0
- package/dist/typography_select.d.ts +26 -0
- package/dist/typography_select.d.ts.map +1 -0
- package/dist/typography_select.js +21 -0
- package/package.json +57 -0
- package/src/theme.css +989 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @abstractuic/ui-kit
|
|
2
|
+
|
|
3
|
+
Shared theme tokens + small UI primitives used across AbstractUIC packages and host apps.
|
|
4
|
+
|
|
5
|
+
This package provides:
|
|
6
|
+
|
|
7
|
+
- **Theme tokens** (CSS variables + theme classes): `ui-kit/src/theme.css`
|
|
8
|
+
- **Theme + typography helpers**: `applyTheme(...)`, `applyTypography(...)`
|
|
9
|
+
- **Common inputs**: `AfSelect`, `ThemeSelect`, `ProviderModelSelect`, etc.
|
|
10
|
+
- **Icons**: `Icon` (used by `@abstractuic/panel-chat`)
|
|
11
|
+
|
|
12
|
+
## Install / peer dependencies
|
|
13
|
+
|
|
14
|
+
This is a React package with peer dependencies on `react@^18` and `react-dom@^18` (see `ui-kit/package.json`).
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
- Workspace: add a dependency on `@abstractuic/ui-kit`
|
|
19
|
+
- npm (once published): `npm i @abstractuic/ui-kit`
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
Import the theme tokens once in your app:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import "@abstractuic/ui-kit/src/theme.css";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Apply a theme at runtime (optional):
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { applyTheme } from "@abstractuic/ui-kit";
|
|
33
|
+
|
|
34
|
+
applyTheme("dark"); // sets a `theme-*` class on <html>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Use UI components:
|
|
38
|
+
|
|
39
|
+
```tsx
|
|
40
|
+
import { ThemeSelect, Icon } from "@abstractuic/ui-kit";
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Exported API
|
|
44
|
+
|
|
45
|
+
See `ui-kit/src/index.ts` for the authoritative export list.
|
|
46
|
+
|
|
47
|
+
## Related docs
|
|
48
|
+
|
|
49
|
+
- Getting started: [`docs/getting-started.md`](../docs/getting-started.md)
|
|
50
|
+
- Repo docs index: [`docs/README.md`](../docs/README.md)
|
|
51
|
+
- Architecture: [`docs/architecture.md`](../docs/architecture.md)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type AfSelectOption = {
|
|
3
|
+
value: string;
|
|
4
|
+
label: string;
|
|
5
|
+
group?: string;
|
|
6
|
+
};
|
|
7
|
+
export type AfSelectProps = {
|
|
8
|
+
value: string;
|
|
9
|
+
options: AfSelectOption[];
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
loading?: boolean;
|
|
13
|
+
searchable?: boolean;
|
|
14
|
+
searchPlaceholder?: string;
|
|
15
|
+
allowCustom?: boolean;
|
|
16
|
+
clearable?: boolean;
|
|
17
|
+
minPopoverWidth?: number;
|
|
18
|
+
variant?: "pin" | "panel";
|
|
19
|
+
className?: string;
|
|
20
|
+
triggerClassName?: string;
|
|
21
|
+
onChange: (value: string) => void;
|
|
22
|
+
renderOption?: (opt: AfSelectOption, state: {
|
|
23
|
+
selected: boolean;
|
|
24
|
+
highlighted: boolean;
|
|
25
|
+
}) => React.ReactNode;
|
|
26
|
+
renderValue?: (opt: AfSelectOption | null, value: string) => React.ReactNode;
|
|
27
|
+
};
|
|
28
|
+
export declare function AfSelect({ value, options, placeholder, disabled, loading, searchable, searchPlaceholder, allowCustom, clearable, minPopoverWidth, variant, className, triggerClassName, onChange, renderOption, renderValue, }: AfSelectProps): React.ReactElement;
|
|
29
|
+
export default AfSelect;
|
|
30
|
+
//# sourceMappingURL=af_select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"af_select.d.ts","sourceRoot":"","sources":["../src/af_select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAOjF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAElC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,EAAE,KAAK,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,OAAO,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IAC5G,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;CAC9E,CAAC;AAEF,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,OAAO,EACP,WAAuB,EACvB,QAAgB,EAChB,OAAe,EACf,UAAiB,EACjB,iBAA6B,EAC7B,WAAmB,EACnB,SAAiB,EACjB,eAAqB,EACrB,OAAiB,EACjB,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,WAAW,GACZ,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CAmQpC;AAED,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { createPortal } from "react-dom";
|
|
4
|
+
function cx(...parts) {
|
|
5
|
+
return parts.filter(Boolean).join(" ");
|
|
6
|
+
}
|
|
7
|
+
export function AfSelect({ value, options, placeholder = "Select…", disabled = false, loading = false, searchable = true, searchPlaceholder = "Search…", allowCustom = false, clearable = false, minPopoverWidth = 240, variant = "panel", className, triggerClassName, onChange, renderOption, renderValue, }) {
|
|
8
|
+
const triggerRef = useRef(null);
|
|
9
|
+
const popoverRef = useRef(null);
|
|
10
|
+
const searchRef = useRef(null);
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
const [search, setSearch] = useState("");
|
|
13
|
+
const [highlightIdx, setHighlightIdx] = useState(0);
|
|
14
|
+
const [placement, setPlacement] = useState("bottom");
|
|
15
|
+
const [pos, setPos] = useState({ left: 0, top: 0, width: 0 });
|
|
16
|
+
const selectedOpt = useMemo(() => {
|
|
17
|
+
const v = String(value ?? "");
|
|
18
|
+
return options.find((o) => o.value === v) || null;
|
|
19
|
+
}, [options, value]);
|
|
20
|
+
const selectedLabel = useMemo(() => {
|
|
21
|
+
return selectedOpt?.label || String(value || "").trim() || "";
|
|
22
|
+
}, [selectedOpt, value]);
|
|
23
|
+
const filtered = useMemo(() => {
|
|
24
|
+
const q = search.trim().toLowerCase();
|
|
25
|
+
if (!q)
|
|
26
|
+
return options;
|
|
27
|
+
return options.filter((o) => o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q));
|
|
28
|
+
}, [options, search]);
|
|
29
|
+
const customOption = useMemo(() => {
|
|
30
|
+
if (!allowCustom)
|
|
31
|
+
return null;
|
|
32
|
+
const q = search.trim();
|
|
33
|
+
if (!q)
|
|
34
|
+
return null;
|
|
35
|
+
const exists = options.some((o) => o.value === q);
|
|
36
|
+
if (exists)
|
|
37
|
+
return null;
|
|
38
|
+
return { value: q, label: `Use "${q}"` };
|
|
39
|
+
}, [allowCustom, options, search]);
|
|
40
|
+
const visibleOptions = useMemo(() => {
|
|
41
|
+
if (!customOption)
|
|
42
|
+
return filtered;
|
|
43
|
+
return [customOption, ...filtered];
|
|
44
|
+
}, [customOption, filtered]);
|
|
45
|
+
const recalcPosition = useCallback(() => {
|
|
46
|
+
const el = triggerRef.current;
|
|
47
|
+
if (!el)
|
|
48
|
+
return;
|
|
49
|
+
const rect = el.getBoundingClientRect();
|
|
50
|
+
const width = Math.max(rect.width, minPopoverWidth);
|
|
51
|
+
const left = Math.max(8, Math.min(rect.left, window.innerWidth - width - 8));
|
|
52
|
+
const availableBelow = window.innerHeight - rect.bottom;
|
|
53
|
+
const availableAbove = rect.top;
|
|
54
|
+
const nextPlacement = availableBelow >= 220 || availableBelow >= availableAbove ? "bottom" : "top";
|
|
55
|
+
setPlacement(nextPlacement);
|
|
56
|
+
const top = nextPlacement === "bottom" ? rect.bottom + 6 : rect.top - 6;
|
|
57
|
+
setPos({ left, top, width });
|
|
58
|
+
}, [minPopoverWidth]);
|
|
59
|
+
const close = useCallback(() => {
|
|
60
|
+
setOpen(false);
|
|
61
|
+
setSearch("");
|
|
62
|
+
}, []);
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!open)
|
|
65
|
+
return;
|
|
66
|
+
recalcPosition();
|
|
67
|
+
const onResize = () => recalcPosition();
|
|
68
|
+
window.addEventListener("resize", onResize);
|
|
69
|
+
window.addEventListener("scroll", onResize, true);
|
|
70
|
+
return () => {
|
|
71
|
+
window.removeEventListener("resize", onResize);
|
|
72
|
+
window.removeEventListener("scroll", onResize, true);
|
|
73
|
+
};
|
|
74
|
+
}, [open, recalcPosition]);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!open)
|
|
77
|
+
return;
|
|
78
|
+
const onDown = (e) => {
|
|
79
|
+
const t = e.target;
|
|
80
|
+
if (!t)
|
|
81
|
+
return;
|
|
82
|
+
if (triggerRef.current?.contains(t))
|
|
83
|
+
return;
|
|
84
|
+
if (popoverRef.current?.contains(t))
|
|
85
|
+
return;
|
|
86
|
+
close();
|
|
87
|
+
};
|
|
88
|
+
document.addEventListener("mousedown", onDown, true);
|
|
89
|
+
return () => document.removeEventListener("mousedown", onDown, true);
|
|
90
|
+
}, [open, close]);
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!open)
|
|
93
|
+
return;
|
|
94
|
+
const idx = Math.max(0, visibleOptions.findIndex((o) => o.value === value));
|
|
95
|
+
setHighlightIdx(idx === -1 ? 0 : idx);
|
|
96
|
+
if (searchable) {
|
|
97
|
+
setTimeout(() => searchRef.current?.focus(), 0);
|
|
98
|
+
}
|
|
99
|
+
}, [open, searchable, value, visibleOptions]);
|
|
100
|
+
const pick = (v) => {
|
|
101
|
+
onChange(v);
|
|
102
|
+
close();
|
|
103
|
+
};
|
|
104
|
+
const onTriggerKeyDown = (e) => {
|
|
105
|
+
if (disabled)
|
|
106
|
+
return;
|
|
107
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
setOpen((x) => !x);
|
|
110
|
+
}
|
|
111
|
+
else if (e.key === "ArrowDown") {
|
|
112
|
+
e.preventDefault();
|
|
113
|
+
setOpen(true);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
const onPopoverKeyDown = (e) => {
|
|
117
|
+
if (e.key === "Escape") {
|
|
118
|
+
e.preventDefault();
|
|
119
|
+
close();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (e.key === "ArrowDown") {
|
|
123
|
+
e.preventDefault();
|
|
124
|
+
setHighlightIdx((i) => Math.min(i + 1, Math.max(0, visibleOptions.length - 1)));
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (e.key === "ArrowUp") {
|
|
128
|
+
e.preventDefault();
|
|
129
|
+
setHighlightIdx((i) => Math.max(i - 1, 0));
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (e.key === "Enter") {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
const opt = visibleOptions[highlightIdx];
|
|
135
|
+
if (opt)
|
|
136
|
+
pick(opt.value);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const showValue = Boolean(String(value || "").trim());
|
|
140
|
+
const triggerText = showValue ? selectedLabel : placeholder;
|
|
141
|
+
const defaultValueNode = (_jsx("span", { className: cx("af-select-value", !showValue && "af-select-value--placeholder"), children: loading ? "Loading…" : triggerText }));
|
|
142
|
+
const valueNode = renderValue ? renderValue(selectedOpt, String(value || "")) : defaultValueNode;
|
|
143
|
+
return (_jsxs("span", { className: cx("af-select", variant === "pin" ? "af-select--pin" : "af-select--panel", className), children: [_jsxs("button", { ref: triggerRef, type: "button", className: cx("af-select-trigger", triggerClassName), disabled: disabled, onMouseDown: (e) => e.stopPropagation(), onClick: (e) => {
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
if (disabled)
|
|
146
|
+
return;
|
|
147
|
+
setOpen((x) => !x);
|
|
148
|
+
}, onKeyDown: onTriggerKeyDown, "aria-haspopup": "listbox", "aria-expanded": open, children: [valueNode, clearable && showValue ? (_jsx("span", { className: "af-select-clear", role: "button", tabIndex: -1, onMouseDown: (e) => {
|
|
149
|
+
e.preventDefault();
|
|
150
|
+
e.stopPropagation();
|
|
151
|
+
}, onClick: (e) => {
|
|
152
|
+
e.preventDefault();
|
|
153
|
+
e.stopPropagation();
|
|
154
|
+
onChange("");
|
|
155
|
+
close();
|
|
156
|
+
}, title: "Clear", children: "\u00D7" })) : null, _jsx("span", { className: cx("af-select-caret", open && "af-select-caret--open"), children: "\u25BE" })] }), open
|
|
157
|
+
? createPortal(_jsxs("div", { ref: popoverRef, className: cx("af-select-popover", placement === "top" && "af-select-popover--top"), style: { position: "fixed", left: `${pos.left}px`, top: `${pos.top}px`, width: `${pos.width}px` }, onMouseDown: (e) => e.stopPropagation(), onClick: (e) => e.stopPropagation(), onKeyDown: onPopoverKeyDown, role: "listbox", tabIndex: -1, children: [searchable ? (_jsx("div", { className: "af-select-search", children: _jsx("input", { ref: searchRef, className: "af-select-search-input", value: search, onChange: (e) => setSearch(e.target.value), placeholder: searchPlaceholder, onKeyDown: onPopoverKeyDown }) })) : null, _jsx("div", { className: "af-select-options", children: visibleOptions.length === 0 ? (_jsx("div", { className: "af-select-empty", children: "No results" })) : (visibleOptions.map((o, i) => {
|
|
158
|
+
const isSelected = o.value === value;
|
|
159
|
+
const isHighlighted = i === highlightIdx;
|
|
160
|
+
const group = String(o.group || "").trim();
|
|
161
|
+
const prevGroup = i > 0 ? String(visibleOptions[i - 1]?.group || "").trim() : "";
|
|
162
|
+
const showGroup = Boolean(group) && group !== prevGroup;
|
|
163
|
+
return (_jsxs(React.Fragment, { children: [showGroup ? _jsx("div", { className: "af-select-group", children: group }) : null, _jsx("div", { className: cx("af-select-option", isSelected && "af-select-option--selected", isHighlighted && "af-select-option--highlighted"), onMouseEnter: () => setHighlightIdx(i), onMouseDown: (e) => {
|
|
164
|
+
e.preventDefault();
|
|
165
|
+
e.stopPropagation();
|
|
166
|
+
}, onClick: () => pick(o.value), children: renderOption ? (renderOption(o, { selected: isSelected, highlighted: isHighlighted })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "af-select-option-label", children: o.label }), isSelected ? _jsx("span", { className: "af-select-check", children: "\u2713" }) : null] })) })] }, o.value));
|
|
167
|
+
})) })] }), document.body)
|
|
168
|
+
: null] }));
|
|
169
|
+
}
|
|
170
|
+
export default AfSelect;
|
package/dist/icon.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type IconName = "chat" | "plus" | "history" | "refresh" | "settings" | "user" | "bot" | "paperclip" | "mic" | "speaker" | "pause" | "terminal" | "edit" | "download" | "loader" | "info" | "warning" | "error" | "copy" | "check" | "x" | "chevronDown" | "chevronRight" | "trash" | "send";
|
|
3
|
+
export declare function Icon({ name, size, className, title, ...props }: {
|
|
4
|
+
name: IconName;
|
|
5
|
+
size?: number;
|
|
6
|
+
title?: string;
|
|
7
|
+
} & Omit<React.SVGProps<SVGSVGElement>, "children">): React.ReactElement;
|
|
8
|
+
//# sourceMappingURL=icon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.d.ts","sourceRoot":"","sources":["../src/icon.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,MAAM,GACN,SAAS,GACT,SAAS,GACT,UAAU,GACV,MAAM,GACN,KAAK,GACL,WAAW,GACX,KAAK,GACL,SAAS,GACT,OAAO,GACP,UAAU,GACV,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,MAAM,GACN,OAAO,GACP,GAAG,GACH,aAAa,GACb,cAAc,GACd,OAAO,GACP,MAAM,CAAC;AAgLX,wBAAgB,IAAI,CAAC,EACnB,IAAI,EACJ,IAAS,EACT,SAAS,EACT,KAAK,EACL,GAAG,KAAK,EACT,EAAE;IACD,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,CAAC,GAAG,KAAK,CAAC,YAAY,CAsBvE"}
|
package/dist/icon.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
function paths(name) {
|
|
3
|
+
switch (name) {
|
|
4
|
+
case "chat":
|
|
5
|
+
return _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" });
|
|
6
|
+
case "plus":
|
|
7
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M12 5v14" }), _jsx("path", { d: "M5 12h14" })] }));
|
|
8
|
+
case "history":
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "9" }), _jsx("path", { d: "M12 7v5l3 3" })] }));
|
|
10
|
+
case "refresh":
|
|
11
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M21 12a9 9 0 0 0-15.5-6.4L3 8" }), _jsx("path", { d: "M3 3v5h5" }), _jsx("path", { d: "M3 12a9 9 0 0 0 15.5 6.4L21 16" }), _jsx("path", { d: "M21 21v-5h-5" })] }));
|
|
12
|
+
case "settings":
|
|
13
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M4 6h16" }), _jsx("circle", { cx: "8", cy: "6", r: "2" }), _jsx("path", { d: "M4 12h16" }), _jsx("circle", { cx: "16", cy: "12", r: "2" }), _jsx("path", { d: "M4 18h16" }), _jsx("circle", { cx: "10", cy: "18", r: "2" })] }));
|
|
14
|
+
case "user":
|
|
15
|
+
return (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "8", r: "4" }), _jsx("path", { d: "M4 21a8 8 0 0 1 16 0" })] }));
|
|
16
|
+
case "bot":
|
|
17
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M12 2v3" }), _jsx("rect", { x: "5", y: "6", width: "14", height: "14", rx: "3" }), _jsx("circle", { cx: "9", cy: "13", r: "1", fill: "currentColor", stroke: "none" }), _jsx("circle", { cx: "15", cy: "13", r: "1", fill: "currentColor", stroke: "none" }), _jsx("path", { d: "M9 17h6" })] }));
|
|
18
|
+
case "paperclip":
|
|
19
|
+
return (_jsx("path", { d: "M21.44 11.05l-9.19 9.19a5.5 5.5 0 0 1-7.78-7.78l9.19-9.19a3.5 3.5 0 0 1 4.95 4.95l-9.2 9.19a1.5 1.5 0 0 1-2.12-2.12l8.49-8.48" }));
|
|
20
|
+
case "mic":
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M12 14a3 3 0 0 0 3-3V5a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3z" }), _jsx("path", { d: "M19 11a7 7 0 0 1-14 0" }), _jsx("path", { d: "M12 18v4" }), _jsx("path", { d: "M8 22h8" })] }));
|
|
22
|
+
case "speaker":
|
|
23
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M11 5L6 9H2v6h4l5 4V5z" }), _jsx("path", { d: "M15.5 8.5a5 5 0 0 1 0 7" }), _jsx("path", { d: "M18 6a9 9 0 0 1 0 12" })] }));
|
|
24
|
+
case "pause":
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: "6", y: "4", width: "4", height: "16", rx: "1", fill: "currentColor", stroke: "none" }), _jsx("rect", { x: "14", y: "4", width: "4", height: "16", rx: "1", fill: "currentColor", stroke: "none" })] }));
|
|
26
|
+
case "terminal":
|
|
27
|
+
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2", ry: "2" }), _jsx("path", { d: "M7 9l3 3-3 3" }), _jsx("path", { d: "M12 15h5" })] }));
|
|
28
|
+
case "edit":
|
|
29
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M12 20h9" }), _jsx("path", { d: "M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })] }));
|
|
30
|
+
case "download":
|
|
31
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }), _jsx("path", { d: "M7 10l5 5 5-5" }), _jsx("path", { d: "M12 15V3" })] }));
|
|
32
|
+
case "loader":
|
|
33
|
+
return _jsx("path", { d: "M21 12a9 9 0 1 1-3.3-6.9" });
|
|
34
|
+
case "info":
|
|
35
|
+
return (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M12 16v-4" }), _jsx("circle", { cx: "12", cy: "8", r: "0.75", fill: "currentColor", stroke: "none" })] }));
|
|
36
|
+
case "warning":
|
|
37
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M12 3l10 18H2L12 3z" }), _jsx("path", { d: "M12 9v4" }), _jsx("circle", { cx: "12", cy: "17", r: "0.75", fill: "currentColor", stroke: "none" })] }));
|
|
38
|
+
case "error":
|
|
39
|
+
return (_jsxs(_Fragment, { children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M15 9l-6 6" }), _jsx("path", { d: "M9 9l6 6" })] }));
|
|
40
|
+
case "copy":
|
|
41
|
+
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2" }), _jsx("path", { d: "M5 15V5a2 2 0 0 1 2-2h10" })] }));
|
|
42
|
+
case "check":
|
|
43
|
+
return _jsx("path", { d: "M20 6L9 17l-5-5" });
|
|
44
|
+
case "x":
|
|
45
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M18 6L6 18" }), _jsx("path", { d: "M6 6l12 12" })] }));
|
|
46
|
+
case "chevronDown":
|
|
47
|
+
return _jsx("path", { d: "M6 9l6 6 6-6" });
|
|
48
|
+
case "chevronRight":
|
|
49
|
+
return _jsx("path", { d: "M9 18l6-6-6-6" });
|
|
50
|
+
case "trash":
|
|
51
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M3 6h18" }), _jsx("path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" }), _jsx("path", { d: "M6 6l1 16h10l1-16" }), _jsx("path", { d: "M10 11v6" }), _jsx("path", { d: "M14 11v6" })] }));
|
|
52
|
+
case "send":
|
|
53
|
+
return (_jsxs(_Fragment, { children: [_jsx("path", { d: "M22 2L11 13" }), _jsx("path", { d: "M22 2L15 22l-4-9-9-4 20-7z" })] }));
|
|
54
|
+
default:
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function Icon({ name, size = 16, className, title, ...props }) {
|
|
59
|
+
const aria_hidden = title ? undefined : true;
|
|
60
|
+
return (_jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className: className, "aria-hidden": aria_hidden, role: title ? "img" : "presentation", ...props, children: [title ? _jsx("title", { children: title }) : null, paths(name)] }));
|
|
61
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { THEMES, THEME_SPECS, applyTheme, getThemeSpec, themeClassName, type ThemeOption, type ThemeSpec } from "./theme";
|
|
2
|
+
export { FONT_SCALES, HEADER_DENSITIES, applyTypography, getFontScaleSpec, getHeaderDensitySpec, type FontScaleOption, type HeaderDensityOption } from "./typography";
|
|
3
|
+
export { AfSelect, type AfSelectProps, type AfSelectOption } from "./af_select";
|
|
4
|
+
export { ProviderModelSelect, type ProviderModelSelectProps, type ProviderOption } from "./provider_model_select";
|
|
5
|
+
export { ThemeSelect, type ThemeSelectProps } from "./theme_select";
|
|
6
|
+
export { FontScaleSelect, HeaderDensitySelect, type FontScaleSelectProps, type HeaderDensitySelectProps } from "./typography_select";
|
|
7
|
+
export { Icon, type IconName } from "./icon";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAC1H,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACtK,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAChF,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAClH,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AACrI,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { THEMES, THEME_SPECS, applyTheme, getThemeSpec, themeClassName } from "./theme";
|
|
2
|
+
export { FONT_SCALES, HEADER_DENSITIES, applyTypography, getFontScaleSpec, getHeaderDensitySpec } from "./typography";
|
|
3
|
+
export { AfSelect } from "./af_select";
|
|
4
|
+
export { ProviderModelSelect } from "./provider_model_select";
|
|
5
|
+
export { ThemeSelect } from "./theme_select";
|
|
6
|
+
export { FontScaleSelect, HeaderDensitySelect } from "./typography_select";
|
|
7
|
+
export { Icon } from "./icon";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type ProviderOption = {
|
|
3
|
+
name: string;
|
|
4
|
+
display_name?: string;
|
|
5
|
+
};
|
|
6
|
+
export type ProviderModelSelectProps = {
|
|
7
|
+
provider: string;
|
|
8
|
+
model: string;
|
|
9
|
+
providers: ProviderOption[];
|
|
10
|
+
models: string[];
|
|
11
|
+
onChange: (next: {
|
|
12
|
+
provider: string;
|
|
13
|
+
model: string;
|
|
14
|
+
}) => void;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
layout?: "stack" | "row";
|
|
17
|
+
className?: string;
|
|
18
|
+
selectClassName?: string;
|
|
19
|
+
allowGatewayDefault?: boolean;
|
|
20
|
+
gatewayDefaultLabel?: string;
|
|
21
|
+
providerLabel?: string;
|
|
22
|
+
modelLabel?: string;
|
|
23
|
+
providerPlaceholder?: string;
|
|
24
|
+
modelPlaceholder?: string;
|
|
25
|
+
loadingProviders?: boolean;
|
|
26
|
+
loadingModels?: boolean;
|
|
27
|
+
providerError?: string;
|
|
28
|
+
modelError?: string;
|
|
29
|
+
allowCustomProvider?: boolean;
|
|
30
|
+
allowCustomModel?: boolean;
|
|
31
|
+
};
|
|
32
|
+
export declare function ProviderModelSelect(props: ProviderModelSelectProps): React.ReactElement;
|
|
33
|
+
//# sourceMappingURL=provider_model_select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider_model_select.d.ts","sourceRoot":"","sources":["../src/provider_model_select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AA6BF,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAE9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAsFvF"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { AfSelect } from "./af_select";
|
|
3
|
+
function normalize_provider_options(items) {
|
|
4
|
+
const seen = new Set();
|
|
5
|
+
const out = [];
|
|
6
|
+
for (const it of items || []) {
|
|
7
|
+
if (!it || typeof it !== "object")
|
|
8
|
+
continue;
|
|
9
|
+
const name = String(it.name || "").trim();
|
|
10
|
+
if (!name || seen.has(name))
|
|
11
|
+
continue;
|
|
12
|
+
seen.add(name);
|
|
13
|
+
out.push({ name, display_name: typeof it.display_name === "string" ? it.display_name : undefined });
|
|
14
|
+
}
|
|
15
|
+
out.sort((a, b) => a.name.localeCompare(b.name));
|
|
16
|
+
return out;
|
|
17
|
+
}
|
|
18
|
+
function normalize_models(items) {
|
|
19
|
+
const seen = new Set();
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const m of items || []) {
|
|
22
|
+
const s = String(m || "").trim();
|
|
23
|
+
if (!s || seen.has(s))
|
|
24
|
+
continue;
|
|
25
|
+
seen.add(s);
|
|
26
|
+
out.push(s);
|
|
27
|
+
}
|
|
28
|
+
out.sort();
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
export function ProviderModelSelect(props) {
|
|
32
|
+
const disabled = props.disabled === true;
|
|
33
|
+
const layout = props.layout || "stack";
|
|
34
|
+
const allow_gateway_default = props.allowGatewayDefault !== false;
|
|
35
|
+
const gateway_default_label = String(props.gatewayDefaultLabel || "(gateway default)");
|
|
36
|
+
const provider_label = String(props.providerLabel || "Provider");
|
|
37
|
+
const model_label = String(props.modelLabel || "Model");
|
|
38
|
+
const provider_placeholder = String(props.providerPlaceholder || "(select)");
|
|
39
|
+
const model_placeholder = String(props.modelPlaceholder || "(select)");
|
|
40
|
+
const provider_value = String(props.provider || "").trim();
|
|
41
|
+
const model_value = String(props.model || "").trim();
|
|
42
|
+
const providers = normalize_provider_options(props.providers);
|
|
43
|
+
const models = normalize_models(props.models);
|
|
44
|
+
const provider_disabled = disabled || Boolean(props.loadingProviders);
|
|
45
|
+
const model_disabled = disabled || Boolean(props.loadingModels);
|
|
46
|
+
const provider_options = [
|
|
47
|
+
...(allow_gateway_default ? [{ value: "", label: gateway_default_label }] : []),
|
|
48
|
+
...providers.map((p) => ({ value: p.name, label: p.display_name ? `${p.display_name} (${p.name})` : p.name })),
|
|
49
|
+
];
|
|
50
|
+
const model_options = [
|
|
51
|
+
...(allow_gateway_default ? [{ value: "", label: gateway_default_label }] : []),
|
|
52
|
+
...models.map((m) => ({ value: m, label: m })),
|
|
53
|
+
];
|
|
54
|
+
return (_jsxs("div", { className: props.className, style: layout === "row"
|
|
55
|
+
? { display: "flex", gap: "10px", flexWrap: "wrap", alignItems: "flex-start" }
|
|
56
|
+
: { display: "flex", flexDirection: "column", gap: "10px" }, children: [_jsxs("div", { style: layout === "row" ? { flex: "1 1 260px", minWidth: 240 } : undefined, children: [_jsx("label", { children: provider_label }), _jsx(AfSelect, { value: provider_value, options: provider_options, placeholder: allow_gateway_default ? gateway_default_label : provider_placeholder, disabled: provider_disabled, loading: Boolean(props.loadingProviders), searchable: true, allowCustom: props.allowCustomProvider === true, clearable: allow_gateway_default, variant: "panel", triggerClassName: props.selectClassName, onChange: (next_provider) => props.onChange({ provider: String(next_provider || "").trim(), model: "" }) }), props.loadingProviders ? _jsx("div", { className: "mono muted", style: { fontSize: "12px", marginTop: "6px" }, children: "Loading providers\u2026" }) : null, props.providerError ? (_jsx("div", { className: "mono", style: { color: "rgba(239, 68, 68, 0.9)", fontSize: "12px", marginTop: "6px" }, children: props.providerError })) : null] }), _jsxs("div", { style: layout === "row" ? { flex: "1 1 260px", minWidth: 240 } : undefined, children: [_jsx("label", { children: model_label }), _jsx(AfSelect, { value: model_value, options: model_options, placeholder: allow_gateway_default ? gateway_default_label : model_placeholder, disabled: model_disabled, loading: Boolean(props.loadingModels), searchable: true, allowCustom: props.allowCustomModel === true, clearable: allow_gateway_default, variant: "panel", triggerClassName: props.selectClassName, onChange: (next_model) => props.onChange({ provider: provider_value, model: String(next_model || "").trim() }) }), props.loadingModels ? _jsx("div", { className: "mono muted", style: { fontSize: "12px", marginTop: "6px" }, children: "Loading models\u2026" }) : null, props.modelError ? (_jsx("div", { className: "mono", style: { color: "rgba(239, 68, 68, 0.9)", fontSize: "12px", marginTop: "6px" }, children: props.modelError })) : null] })] }));
|
|
57
|
+
}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ThemeOption = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
};
|
|
5
|
+
export type ThemeSpec = ThemeOption & {
|
|
6
|
+
group: "dark" | "light";
|
|
7
|
+
swatches: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare const THEME_SPECS: ThemeSpec[];
|
|
10
|
+
export declare const THEMES: ThemeOption[];
|
|
11
|
+
export declare function getThemeSpec(theme_id: string): ThemeSpec | undefined;
|
|
12
|
+
export declare function themeClassName(theme_id: string): string;
|
|
13
|
+
export declare function applyTheme(theme_id: string): void;
|
|
14
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG;IACpC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,SAAS,EA4BlC,CAAC;AAEF,eAAO,MAAM,MAAM,EAAE,WAAW,EAA2D,CAAC;AAE5F,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAGpE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGvD;AAED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CASjD"}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const THEME_SPECS = [
|
|
2
|
+
// Dark (grouped by similarity)
|
|
3
|
+
{ id: "dark", label: "Dark (Abstract)", group: "dark", swatches: ["#1a1a2e", "#16213e", "#0f3460", "#e94560", "#60a5fa", "#27ae60"] },
|
|
4
|
+
{ id: "catppuccin-mocha", label: "Catppuccin Mocha", group: "dark", swatches: ["#1e1e2e", "#181825", "#313244", "#cba6f7", "#89b4fa", "#a6e3a1"] },
|
|
5
|
+
{ id: "catppuccin-macchiato", label: "Catppuccin Macchiato", group: "dark", swatches: ["#24273a", "#1e2030", "#363a4f", "#c6a0f6", "#8aadf4", "#a6da95"] },
|
|
6
|
+
{ id: "catppuccin-frappe", label: "Catppuccin Frappe", group: "dark", swatches: ["#303446", "#292c3c", "#414559", "#ca9ee6", "#8caaee", "#a6d189"] },
|
|
7
|
+
{ id: "rose-pine", label: "Rose Pine", group: "dark", swatches: ["#191724", "#1f1d2e", "#26233a", "#c4a7e7", "#31748f", "#9ccfd8"] },
|
|
8
|
+
{ id: "rose-pine-moon", label: "Rose Pine Moon", group: "dark", swatches: ["#232136", "#2a273f", "#393552", "#c4a7e7", "#3e8fb0", "#9ccfd8"] },
|
|
9
|
+
{ id: "tokyo-night", label: "Tokyo Night", group: "dark", swatches: ["#1a1b26", "#24283b", "#414868", "#7aa2f7", "#2ac3de", "#9ece6a"] },
|
|
10
|
+
{ id: "nord", label: "Nord", group: "dark", swatches: ["#2e3440", "#3b4252", "#434c5e", "#88c0d0", "#5e81ac", "#a3be8c"] },
|
|
11
|
+
{ id: "one-dark", label: "One Dark", group: "dark", swatches: ["#282c34", "#21252b", "#3a3f4b", "#61afef", "#c678dd", "#98c379"] },
|
|
12
|
+
{ id: "dracula", label: "Dracula", group: "dark", swatches: ["#282a36", "#343746", "#44475a", "#ff79c6", "#8be9fd", "#50fa7b"] },
|
|
13
|
+
{ id: "monokai", label: "Monokai", group: "dark", swatches: ["#272822", "#2d2e27", "#3e3d32", "#66d9ef", "#a1efe4", "#a6e22e"] },
|
|
14
|
+
{ id: "gruvbox", label: "Gruvbox", group: "dark", swatches: ["#282828", "#3c3836", "#504945", "#fe8019", "#83a598", "#b8bb26"] },
|
|
15
|
+
{ id: "solarized-dark", label: "Solarized Dark", group: "dark", swatches: ["#002b36", "#073642", "#0b4b5a", "#268bd2", "#2aa198", "#859900"] },
|
|
16
|
+
{ id: "everforest-dark", label: "Everforest Dark", group: "dark", swatches: ["#2b3339", "#323c41", "#3a464c", "#a7c080", "#7fbbb3", "#a7c080"] },
|
|
17
|
+
// Light (grouped by similarity)
|
|
18
|
+
{ id: "catppuccin-latte", label: "Catppuccin Latte", group: "light", swatches: ["#eff1f5", "#e6e9ef", "#ccd0da", "#8839ef", "#1e66f5", "#40a02b"] },
|
|
19
|
+
{ id: "rose-pine-dawn", label: "Rose Pine Dawn", group: "light", swatches: ["#faf4ed", "#fffaf3", "#f2e9e1", "#907aa9", "#56949f", "#286983"] },
|
|
20
|
+
{ id: "one-light", label: "One Light", group: "light", swatches: ["#fafafa", "#ffffff", "#e5e5e6", "#4078f2", "#a626a4", "#50a14f"] },
|
|
21
|
+
{ id: "everforest-light", label: "Everforest Light", group: "light", swatches: ["#fdf6e3", "#fffbef", "#e8e0cc", "#4f8f5a", "#3a94c5", "#4f8f5a"] },
|
|
22
|
+
{ id: "solarized-light", label: "Solarized Light", group: "light", swatches: ["#fdf6e3", "#eee8d5", "#e3ddc9", "#268bd2", "#2aa198", "#859900"] },
|
|
23
|
+
{ id: "light", label: "Light", group: "light", swatches: ["#f7f7fb", "#ffffff", "#e6e8f0", "#e94560", "#2563eb", "#16a34a"] },
|
|
24
|
+
];
|
|
25
|
+
export const THEMES = THEME_SPECS.map((t) => ({ id: t.id, label: t.label }));
|
|
26
|
+
export function getThemeSpec(theme_id) {
|
|
27
|
+
const id = String(theme_id || "").trim();
|
|
28
|
+
return THEME_SPECS.find((t) => t.id === id);
|
|
29
|
+
}
|
|
30
|
+
export function themeClassName(theme_id) {
|
|
31
|
+
const id = String(theme_id || "").trim() || "dark";
|
|
32
|
+
return `theme-${id}`;
|
|
33
|
+
}
|
|
34
|
+
export function applyTheme(theme_id) {
|
|
35
|
+
try {
|
|
36
|
+
const root = document.documentElement;
|
|
37
|
+
const current = Array.from(root.classList).filter((c) => c.startsWith("theme-"));
|
|
38
|
+
for (const c of current)
|
|
39
|
+
root.classList.remove(c);
|
|
40
|
+
root.classList.add(themeClassName(theme_id));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// ignore
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { type ThemeSpec } from "./theme";
|
|
3
|
+
export type ThemeSelectProps = {
|
|
4
|
+
value: string;
|
|
5
|
+
onChange: (theme_id: string) => void;
|
|
6
|
+
themes?: ThemeSpec[];
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
variant?: "panel" | "pin";
|
|
9
|
+
className?: string;
|
|
10
|
+
triggerClassName?: string;
|
|
11
|
+
placeholder?: string;
|
|
12
|
+
showSwatches?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare function ThemeSelect(props: ThemeSelectProps): React.ReactElement;
|
|
15
|
+
export default ThemeSelect;
|
|
16
|
+
//# sourceMappingURL=theme_select.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme_select.d.ts","sourceRoot":"","sources":["../src/theme_select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAe,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAMF,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,KAAK,CAAC,YAAY,CAqEvE;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { AfSelect } from "./af_select";
|
|
4
|
+
import { THEME_SPECS } from "./theme";
|
|
5
|
+
function theme_group_label(group) {
|
|
6
|
+
return group === "light" ? "Light" : "Dark";
|
|
7
|
+
}
|
|
8
|
+
export function ThemeSelect(props) {
|
|
9
|
+
const themes = props.themes && props.themes.length ? props.themes : THEME_SPECS;
|
|
10
|
+
const show_swatches = props.showSwatches !== false;
|
|
11
|
+
const variant = props.variant || "panel";
|
|
12
|
+
const by_id = useMemo(() => {
|
|
13
|
+
const out = {};
|
|
14
|
+
for (const t of themes)
|
|
15
|
+
out[t.id] = t;
|
|
16
|
+
return out;
|
|
17
|
+
}, [themes]);
|
|
18
|
+
const options = useMemo(() => {
|
|
19
|
+
return themes.map((t) => ({ value: t.id, label: t.label, group: theme_group_label(t.group) }));
|
|
20
|
+
}, [themes]);
|
|
21
|
+
return (_jsx(AfSelect, { value: props.value, options: options, placeholder: props.placeholder || "Select theme…", disabled: props.disabled === true, searchable: true, allowCustom: false, clearable: false, variant: variant, className: props.className, triggerClassName: props.triggerClassName, onChange: (next) => props.onChange(String(next || "").trim()), renderValue: (_opt, value) => {
|
|
22
|
+
const id = String(value || "").trim();
|
|
23
|
+
const spec = by_id[id];
|
|
24
|
+
const label = spec?.label || id || (props.placeholder || "Select theme…");
|
|
25
|
+
const sw = spec?.swatches || null;
|
|
26
|
+
const placeholder = !id;
|
|
27
|
+
return (_jsxs("span", { className: "af-theme-value", children: [_jsx("span", { className: `af-theme-label ${placeholder ? "af-theme-label--placeholder" : ""}`.trim(), children: label }), show_swatches && sw ? (_jsx("span", { className: "af-theme-swatches", "aria-hidden": "true", children: sw.map((c, i) => (_jsx("span", { className: "af-theme-swatch", style: { background: c } }, `${id}:${i}`))) })) : null] }));
|
|
28
|
+
}, renderOption: (opt, state) => {
|
|
29
|
+
const id = String(opt.value || "").trim();
|
|
30
|
+
const spec = by_id[id];
|
|
31
|
+
const sw = spec?.swatches || null;
|
|
32
|
+
return (_jsxs(_Fragment, { children: [_jsx("span", { className: "af-theme-option-label", children: opt.label }), _jsxs("span", { className: "af-theme-option-right", children: [show_swatches && sw ? (_jsx("span", { className: "af-theme-swatches", "aria-hidden": "true", children: sw.map((c, i) => (_jsx("span", { className: "af-theme-swatch", style: { background: c } }, `${id}:${i}`))) })) : null, state.selected ? _jsx("span", { className: "af-select-check", children: "\u2713" }) : null] })] }));
|
|
33
|
+
} }));
|
|
34
|
+
}
|
|
35
|
+
export default ThemeSelect;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type FontScaleOption = {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
scale: number;
|
|
5
|
+
};
|
|
6
|
+
export type HeaderDensityOption = {
|
|
7
|
+
id: string;
|
|
8
|
+
label: string;
|
|
9
|
+
density: number;
|
|
10
|
+
};
|
|
11
|
+
export declare const FONT_SCALES: FontScaleOption[];
|
|
12
|
+
export declare const HEADER_DENSITIES: HeaderDensityOption[];
|
|
13
|
+
export declare function getFontScaleSpec(font_scale_id: string): FontScaleOption;
|
|
14
|
+
export declare function getHeaderDensitySpec(header_density_id: string): HeaderDensityOption;
|
|
15
|
+
export declare function applyTypography(opts: {
|
|
16
|
+
font_scale?: string;
|
|
17
|
+
header_density?: string;
|
|
18
|
+
}): void;
|
|
19
|
+
//# sourceMappingURL=typography.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typography.d.ts","sourceRoot":"","sources":["../src/typography.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,eAAO,MAAM,WAAW,EAAE,eAAe,EAKxC,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,mBAAmB,EAIjD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,aAAa,EAAE,MAAM,GAAG,eAAe,CAGvE;AAED,wBAAgB,oBAAoB,CAAC,iBAAiB,EAAE,MAAM,GAAG,mBAAmB,CAGnF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAU5F"}
|