@abstractframework/ui-kit 0.1.2 → 0.1.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/README.md +9 -3
- package/dist/af_select.d.ts +7 -1
- package/dist/af_select.d.ts.map +1 -1
- package/dist/af_select.js +16 -6
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -7
- package/dist/provider_model_select.js +1 -1
- package/dist/theme_select.d.ts +1 -1
- package/dist/theme_select.d.ts.map +1 -1
- package/dist/theme_select.js +2 -2
- package/dist/tool_policy_editor.d.ts +35 -0
- package/dist/tool_policy_editor.d.ts.map +1 -0
- package/dist/tool_policy_editor.js +161 -0
- package/dist/typography_select.d.ts +1 -1
- package/dist/typography_select.d.ts.map +1 -1
- package/dist/typography_select.js +2 -2
- package/package.json +3 -3
- package/src/theme.css +280 -0
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ This package provides:
|
|
|
6
6
|
|
|
7
7
|
- **Theme tokens** (CSS variables + theme classes): `ui-kit/src/theme.css`
|
|
8
8
|
- **Theme + typography helpers**: `applyTheme(...)`, `applyTypography(...)`
|
|
9
|
-
- **Common inputs**: `AfSelect`, `ThemeSelect`, `ProviderModelSelect`, etc.
|
|
9
|
+
- **Common inputs**: `AfSelect`, `ThemeSelect`, `ProviderModelSelect`, `ToolPolicyEditor`, etc.
|
|
10
10
|
- **Icons**: `Icon` (used by `@abstractframework/panel-chat`)
|
|
11
11
|
|
|
12
12
|
## Install / peer dependencies
|
|
@@ -16,7 +16,7 @@ This is a React package with peer dependencies on `react@^18` and `react-dom@^18
|
|
|
16
16
|
## Install
|
|
17
17
|
|
|
18
18
|
- Workspace: add a dependency on `@abstractframework/ui-kit`
|
|
19
|
-
- npm
|
|
19
|
+
- npm: `npm i @abstractframework/ui-kit`
|
|
20
20
|
|
|
21
21
|
## Usage
|
|
22
22
|
|
|
@@ -37,9 +37,15 @@ applyTheme("dark"); // sets a `theme-*` class on <html>
|
|
|
37
37
|
Use UI components:
|
|
38
38
|
|
|
39
39
|
```tsx
|
|
40
|
-
import { ThemeSelect, Icon } from "@abstractframework/ui-kit";
|
|
40
|
+
import { ThemeSelect, Icon, ToolPolicyEditor } from "@abstractframework/ui-kit";
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
### Tool policy editor
|
|
44
|
+
|
|
45
|
+
`ToolPolicyEditor` renders the shared allowlist + approve/ask picker for gateway tools. It intentionally **does not** include a deny mode; tools are denied by removing them from the allowlist. Pass `toolMode` (and optional `toolModeLabel`/`toolModeDetail`) to surface the gateway tool execution mode in a prominent banner.
|
|
46
|
+
|
|
47
|
+
The default approve/ask classification is exposed as `TOOL_POLICY_DEFAULTS` (mirrors the AbstractRuntime `ToolApprovalPolicy` defaults).
|
|
48
|
+
|
|
43
49
|
## Exported API
|
|
44
50
|
|
|
45
51
|
See `ui-kit/src/index.ts` for the authoritative export list.
|
package/dist/af_select.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export type AfSelectOption = {
|
|
|
3
3
|
value: string;
|
|
4
4
|
label: string;
|
|
5
5
|
group?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
reason?: string;
|
|
6
8
|
};
|
|
7
9
|
export type AfSelectProps = {
|
|
8
10
|
value: string;
|
|
@@ -18,6 +20,10 @@ export type AfSelectProps = {
|
|
|
18
20
|
variant?: "pin" | "panel";
|
|
19
21
|
className?: string;
|
|
20
22
|
triggerClassName?: string;
|
|
23
|
+
customOptionLabel?: (value: string) => string;
|
|
24
|
+
validateCustomValue?: (value: string, context: {
|
|
25
|
+
options: AfSelectOption[];
|
|
26
|
+
}) => string | null | undefined;
|
|
21
27
|
onChange: (value: string) => void;
|
|
22
28
|
renderOption?: (opt: AfSelectOption, state: {
|
|
23
29
|
selected: boolean;
|
|
@@ -25,6 +31,6 @@ export type AfSelectProps = {
|
|
|
25
31
|
}) => React.ReactNode;
|
|
26
32
|
renderValue?: (opt: AfSelectOption | null, value: string) => React.ReactNode;
|
|
27
33
|
};
|
|
28
|
-
export declare function AfSelect({ value, options, placeholder, disabled, loading, searchable, searchPlaceholder, allowCustom, clearable, minPopoverWidth, variant, className, triggerClassName, onChange, renderOption, renderValue, }: AfSelectProps): React.ReactElement;
|
|
34
|
+
export declare function AfSelect({ value, options, placeholder, disabled, loading, searchable, searchPlaceholder, allowCustom, clearable, minPopoverWidth, variant, className, triggerClassName, customOptionLabel, validateCustomValue, onChange, renderOption, renderValue, }: AfSelectProps): React.ReactElement;
|
|
29
35
|
export default AfSelect;
|
|
30
36
|
//# sourceMappingURL=af_select.d.ts.map
|
package/dist/af_select.d.ts.map
CHANGED
|
@@ -1 +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;
|
|
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;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,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,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9C,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,OAAO,EAAE,cAAc,EAAE,CAAA;KAAE,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC3G,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,iBAAiB,EACjB,mBAAmB,EACnB,QAAQ,EACR,YAAY,EACZ,WAAW,GACZ,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CA4RpC;AAED,eAAe,QAAQ,CAAC"}
|
package/dist/af_select.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createPortal } from "react-dom";
|
|
|
4
4
|
function cx(...parts) {
|
|
5
5
|
return parts.filter(Boolean).join(" ");
|
|
6
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, }) {
|
|
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, customOptionLabel, validateCustomValue, onChange, renderOption, renderValue, }) {
|
|
8
8
|
const triggerRef = useRef(null);
|
|
9
9
|
const popoverRef = useRef(null);
|
|
10
10
|
const searchRef = useRef(null);
|
|
@@ -35,8 +35,14 @@ export function AfSelect({ value, options, placeholder = "Select…", disabled =
|
|
|
35
35
|
const exists = options.some((o) => o.value === q);
|
|
36
36
|
if (exists)
|
|
37
37
|
return null;
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
const reason = validateCustomValue?.(q, { options }) || "";
|
|
39
|
+
return {
|
|
40
|
+
value: q,
|
|
41
|
+
label: customOptionLabel ? customOptionLabel(q) : `Use "${q}"`,
|
|
42
|
+
disabled: Boolean(reason),
|
|
43
|
+
reason,
|
|
44
|
+
};
|
|
45
|
+
}, [allowCustom, customOptionLabel, options, search, validateCustomValue]);
|
|
40
46
|
const visibleOptions = useMemo(() => {
|
|
41
47
|
if (!customOption)
|
|
42
48
|
return filtered;
|
|
@@ -98,6 +104,9 @@ export function AfSelect({ value, options, placeholder = "Select…", disabled =
|
|
|
98
104
|
}
|
|
99
105
|
}, [open, searchable, value, visibleOptions]);
|
|
100
106
|
const pick = (v) => {
|
|
107
|
+
const option = visibleOptions.find((o) => o.value === v);
|
|
108
|
+
if (option?.disabled)
|
|
109
|
+
return;
|
|
101
110
|
onChange(v);
|
|
102
111
|
close();
|
|
103
112
|
};
|
|
@@ -138,6 +147,7 @@ export function AfSelect({ value, options, placeholder = "Select…", disabled =
|
|
|
138
147
|
};
|
|
139
148
|
const showValue = Boolean(String(value || "").trim());
|
|
140
149
|
const triggerText = showValue ? selectedLabel : placeholder;
|
|
150
|
+
const customError = customOption?.disabled ? customOption.reason || "Unavailable" : "";
|
|
141
151
|
const defaultValueNode = (_jsx("span", { className: cx("af-select-value", !showValue && "af-select-value--placeholder"), children: loading ? "Loading…" : triggerText }));
|
|
142
152
|
const valueNode = renderValue ? renderValue(selectedOpt, String(value || "")) : defaultValueNode;
|
|
143
153
|
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) => {
|
|
@@ -154,16 +164,16 @@ export function AfSelect({ value, options, placeholder = "Select…", disabled =
|
|
|
154
164
|
onChange("");
|
|
155
165
|
close();
|
|
156
166
|
}, 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 ? (
|
|
167
|
+
? 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(), onWheel: (e) => e.stopPropagation(), onKeyDown: onPopoverKeyDown, role: "listbox", tabIndex: -1, children: [searchable ? (_jsxs("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, "aria-invalid": Boolean(customError), "aria-describedby": customError ? "af-select-custom-error" : undefined }), customError ? (_jsx("div", { id: "af-select-custom-error", className: "af-select-custom-error", role: "alert", children: customError })) : null] })) : null, _jsx("div", { className: "af-select-options", children: loading && visibleOptions.length === 0 ? (_jsx("div", { className: "af-select-empty", children: "Loading\u2026" })) : visibleOptions.length === 0 ? (_jsx("div", { className: "af-select-empty", children: "No results" })) : (visibleOptions.map((o, i) => {
|
|
158
168
|
const isSelected = o.value === value;
|
|
159
169
|
const isHighlighted = i === highlightIdx;
|
|
160
170
|
const group = String(o.group || "").trim();
|
|
161
171
|
const prevGroup = i > 0 ? String(visibleOptions[i - 1]?.group || "").trim() : "";
|
|
162
172
|
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) => {
|
|
173
|
+
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", o.disabled && "af-select-option--disabled"), title: o.disabled ? o.reason || "Unavailable" : undefined, onMouseEnter: () => setHighlightIdx(i), onMouseDown: (e) => {
|
|
164
174
|
e.preventDefault();
|
|
165
175
|
e.stopPropagation();
|
|
166
|
-
}, onClick: () => pick(o.value), children: renderOption ? (renderOption(o, { selected: isSelected, highlighted: isHighlighted })) : (_jsxs(_Fragment, { children: [
|
|
176
|
+
}, onClick: () => pick(o.value), "aria-disabled": o.disabled ? true : undefined, children: renderOption ? (renderOption(o, { selected: isSelected, highlighted: isHighlighted })) : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "af-select-option-label", children: [_jsx("span", { children: o.label }), o.reason ? _jsx("small", { className: "af-select-option-reason", children: o.reason }) : null] }), isSelected ? _jsx("span", { className: "af-select-check", children: "\u2713" }) : null] })) })] }, o.value));
|
|
167
177
|
})) })] }), document.body)
|
|
168
178
|
: null] }));
|
|
169
179
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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";
|
|
1
|
+
export { THEMES, THEME_SPECS, applyTheme, getThemeSpec, themeClassName, type ThemeOption, type ThemeSpec } from "./theme.js";
|
|
2
|
+
export { FONT_SCALES, HEADER_DENSITIES, applyTypography, getFontScaleSpec, getHeaderDensitySpec, type FontScaleOption, type HeaderDensityOption } from "./typography.js";
|
|
3
|
+
export { AfSelect, type AfSelectProps, type AfSelectOption } from "./af_select.js";
|
|
4
|
+
export { ProviderModelSelect, type ProviderModelSelectProps, type ProviderOption } from "./provider_model_select.js";
|
|
5
|
+
export { ThemeSelect, type ThemeSelectProps } from "./theme_select.js";
|
|
6
|
+
export { FontScaleSelect, HeaderDensitySelect, type FontScaleSelectProps, type HeaderDensitySelectProps } from "./typography_select.js";
|
|
7
|
+
export { Icon, type IconName } from "./icon.js";
|
|
8
|
+
export { ToolPolicyEditor, TOOL_POLICY_DEFAULTS, type ToolPolicyEditorProps, type ToolPolicySelection, type ToolPolicyDefaults, type ToolSpec, type ToolApprovalMode, } from "./tool_policy_editor.js";
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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,
|
|
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,YAAY,CAAC;AAC7H,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACzK,OAAO,EAAE,QAAQ,EAAE,KAAK,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACnF,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,KAAK,cAAc,EAAE,MAAM,4BAA4B,CAAC;AACrH,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AACxI,OAAO,EAAE,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACb,KAAK,gBAAgB,GACtB,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
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";
|
|
1
|
+
export { THEMES, THEME_SPECS, applyTheme, getThemeSpec, themeClassName } from "./theme.js";
|
|
2
|
+
export { FONT_SCALES, HEADER_DENSITIES, applyTypography, getFontScaleSpec, getHeaderDensitySpec } from "./typography.js";
|
|
3
|
+
export { AfSelect } from "./af_select.js";
|
|
4
|
+
export { ProviderModelSelect } from "./provider_model_select.js";
|
|
5
|
+
export { ThemeSelect } from "./theme_select.js";
|
|
6
|
+
export { FontScaleSelect, HeaderDensitySelect } from "./typography_select.js";
|
|
7
|
+
export { Icon } from "./icon.js";
|
|
8
|
+
export { ToolPolicyEditor, TOOL_POLICY_DEFAULTS, } from "./tool_policy_editor.js";
|
package/dist/theme_select.d.ts
CHANGED
|
@@ -1 +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,
|
|
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,YAAY,CAAC;AAEzD,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"}
|
package/dist/theme_select.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { AfSelect } from "./af_select";
|
|
4
|
-
import { THEME_SPECS } from "./theme";
|
|
3
|
+
import { AfSelect } from "./af_select.js";
|
|
4
|
+
import { THEME_SPECS } from "./theme.js";
|
|
5
5
|
function theme_group_label(group) {
|
|
6
6
|
return group === "light" ? "Light" : "Dark";
|
|
7
7
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export type ToolSpec = {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
toolset?: string;
|
|
6
|
+
when_to_use?: string;
|
|
7
|
+
};
|
|
8
|
+
export type ToolApprovalMode = "approve" | "ask";
|
|
9
|
+
export type ToolPolicyDefaults = {
|
|
10
|
+
autoApprove: string[];
|
|
11
|
+
requireApproval: string[];
|
|
12
|
+
};
|
|
13
|
+
export type ToolPolicySelection = {
|
|
14
|
+
mode: "all" | "custom";
|
|
15
|
+
selected: string[];
|
|
16
|
+
approval: Record<string, ToolApprovalMode>;
|
|
17
|
+
};
|
|
18
|
+
export type ToolPolicyEditorProps = {
|
|
19
|
+
tools: ToolSpec[];
|
|
20
|
+
value: ToolPolicySelection;
|
|
21
|
+
onChange: (next: ToolPolicySelection) => void;
|
|
22
|
+
defaults?: ToolPolicyDefaults;
|
|
23
|
+
disabled?: boolean;
|
|
24
|
+
title?: string;
|
|
25
|
+
subtitle?: string;
|
|
26
|
+
note?: string;
|
|
27
|
+
toolMode?: string;
|
|
28
|
+
toolModeLabel?: string;
|
|
29
|
+
toolModeDetail?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare const TOOL_POLICY_DEFAULTS: ToolPolicyDefaults;
|
|
33
|
+
export declare function ToolPolicyEditor(props: ToolPolicyEditorProps): React.ReactElement;
|
|
34
|
+
export default ToolPolicyEditor;
|
|
35
|
+
//# sourceMappingURL=tool_policy_editor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_policy_editor.d.ts","sourceRoot":"","sources":["../src/tool_policy_editor.tsx"],"names":[],"mappings":"AAGA,OAAO,KAA4B,MAAM,OAAO,CAAC;AAEjD,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,KAAK,CAAC;AAEjD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAGF,eAAO,MAAM,oBAAoB,EAAE,kBAwBlC,CAAC;AAgEF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CA4KjF;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* ToolPolicyEditor: shared allowlist + approval selector for thin clients.
|
|
4
|
+
*/
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
// Mirrors AbstractRuntime ToolApprovalPolicy defaults (keep in sync).
|
|
7
|
+
export const TOOL_POLICY_DEFAULTS = {
|
|
8
|
+
autoApprove: [
|
|
9
|
+
"list_files",
|
|
10
|
+
"skim_folders",
|
|
11
|
+
"analyze_code",
|
|
12
|
+
"read_file",
|
|
13
|
+
"skim_files",
|
|
14
|
+
"search_files",
|
|
15
|
+
"open_attachment",
|
|
16
|
+
"web_search",
|
|
17
|
+
"skim_websearch",
|
|
18
|
+
"skim_url",
|
|
19
|
+
"fetch_url",
|
|
20
|
+
"list_email_accounts",
|
|
21
|
+
"list_emails",
|
|
22
|
+
"read_email",
|
|
23
|
+
"list_whatsapp_messages",
|
|
24
|
+
"read_whatsapp_message",
|
|
25
|
+
"send_email",
|
|
26
|
+
"send_whatsapp_message",
|
|
27
|
+
"send_telegram_message",
|
|
28
|
+
"send_telegram_artifact",
|
|
29
|
+
],
|
|
30
|
+
requireApproval: ["write_file", "edit_file", "execute_command"],
|
|
31
|
+
};
|
|
32
|
+
function normalize_tools(items) {
|
|
33
|
+
const seen = new Set();
|
|
34
|
+
const out = [];
|
|
35
|
+
for (const it of items || []) {
|
|
36
|
+
if (!it)
|
|
37
|
+
continue;
|
|
38
|
+
const name = String(it.name || "").trim();
|
|
39
|
+
if (!name || seen.has(name))
|
|
40
|
+
continue;
|
|
41
|
+
seen.add(name);
|
|
42
|
+
out.push({
|
|
43
|
+
name,
|
|
44
|
+
description: typeof it.description === "string" ? it.description : undefined,
|
|
45
|
+
toolset: typeof it.toolset === "string" ? it.toolset : undefined,
|
|
46
|
+
when_to_use: typeof it.when_to_use === "string" ? it.when_to_use : undefined,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
out.sort((a, b) => {
|
|
50
|
+
const a_set = String(a.toolset || "");
|
|
51
|
+
const b_set = String(b.toolset || "");
|
|
52
|
+
if (a_set !== b_set)
|
|
53
|
+
return a_set.localeCompare(b_set);
|
|
54
|
+
return a.name.localeCompare(b.name);
|
|
55
|
+
});
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function default_mode_for(name, defaults) {
|
|
59
|
+
if (defaults.requireApproval.includes(name))
|
|
60
|
+
return "ask";
|
|
61
|
+
if (defaults.autoApprove.includes(name))
|
|
62
|
+
return "approve";
|
|
63
|
+
return "ask";
|
|
64
|
+
}
|
|
65
|
+
function describe_tool_mode(raw, override_label, override_detail) {
|
|
66
|
+
const mode = String(raw || "").trim().toLowerCase();
|
|
67
|
+
if (!mode && !override_label && !override_detail)
|
|
68
|
+
return null;
|
|
69
|
+
let info;
|
|
70
|
+
if (mode === "approval" || mode === "local_approval" || mode === "local-approval") {
|
|
71
|
+
info = { label: "APPROVAL", detail: "Safe tools auto-run; mutating tools ask for approval.", tone: "safe" };
|
|
72
|
+
}
|
|
73
|
+
else if (mode === "passthrough") {
|
|
74
|
+
info = { label: "PASSTHROUGH", detail: "All tools require approval before execution.", tone: "warn" };
|
|
75
|
+
}
|
|
76
|
+
else if (mode === "delegated" || mode === "delegate" || mode === "job") {
|
|
77
|
+
info = { label: "DELEGATED", detail: "Tool calls wait for external executors.", tone: "info" };
|
|
78
|
+
}
|
|
79
|
+
else if (mode === "local" || mode === "local_all" || mode === "local-all") {
|
|
80
|
+
info = { label: "LOCAL", detail: "All tools run locally; client policy may still require approval.", tone: "danger" };
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
info = { label: "UNKNOWN", detail: "#FALLBACK: gateway tool mode not reported.", tone: "warn" };
|
|
84
|
+
}
|
|
85
|
+
if (override_label)
|
|
86
|
+
info = { ...info, label: override_label };
|
|
87
|
+
if (override_detail)
|
|
88
|
+
info = { ...info, detail: override_detail };
|
|
89
|
+
return info;
|
|
90
|
+
}
|
|
91
|
+
export function ToolPolicyEditor(props) {
|
|
92
|
+
const defaults = props.defaults || TOOL_POLICY_DEFAULTS;
|
|
93
|
+
const disabled = props.disabled === true;
|
|
94
|
+
const title = props.title || "Tools";
|
|
95
|
+
const subtitle = props.subtitle ||
|
|
96
|
+
"Default is all tools. Safe/read-only tools auto-approve; mutating tools ask for approval.";
|
|
97
|
+
const note = String(props.note || "").trim();
|
|
98
|
+
const tool_mode = useMemo(() => describe_tool_mode(props.toolMode, props.toolModeLabel, props.toolModeDetail), [props.toolMode, props.toolModeLabel, props.toolModeDetail]);
|
|
99
|
+
const tools = useMemo(() => normalize_tools(props.tools), [props.tools]);
|
|
100
|
+
const all_names = useMemo(() => tools.map((t) => t.name), [tools]);
|
|
101
|
+
const all_names_set = useMemo(() => new Set(all_names), [all_names]);
|
|
102
|
+
const [filter, setFilter] = useState("");
|
|
103
|
+
const selected = useMemo(() => {
|
|
104
|
+
const raw = Array.isArray(props.value?.selected) ? props.value.selected : [];
|
|
105
|
+
return raw.map((n) => String(n || "").trim()).filter((n) => n && all_names_set.has(n));
|
|
106
|
+
}, [props.value?.selected, all_names_set]);
|
|
107
|
+
const mode = props.value?.mode === "custom" ? "custom" : "all";
|
|
108
|
+
const filtered = useMemo(() => {
|
|
109
|
+
const q = filter.trim().toLowerCase();
|
|
110
|
+
if (!q)
|
|
111
|
+
return tools;
|
|
112
|
+
return tools.filter((t) => {
|
|
113
|
+
const name = t.name.toLowerCase();
|
|
114
|
+
const desc = (t.description || "").toLowerCase();
|
|
115
|
+
return name.includes(q) || desc.includes(q);
|
|
116
|
+
});
|
|
117
|
+
}, [tools, filter]);
|
|
118
|
+
const effective_selected = mode === "all" ? new Set(all_names) : new Set(selected);
|
|
119
|
+
const on_change = (next) => {
|
|
120
|
+
props.onChange(next);
|
|
121
|
+
};
|
|
122
|
+
const toggle_mode = (next) => {
|
|
123
|
+
if (disabled)
|
|
124
|
+
return;
|
|
125
|
+
on_change({ ...props.value, mode: next, selected });
|
|
126
|
+
};
|
|
127
|
+
const toggle_tool = (name, enabled) => {
|
|
128
|
+
if (disabled)
|
|
129
|
+
return;
|
|
130
|
+
const set = new Set(selected);
|
|
131
|
+
if (enabled)
|
|
132
|
+
set.add(name);
|
|
133
|
+
else
|
|
134
|
+
set.delete(name);
|
|
135
|
+
on_change({ ...props.value, selected: Array.from(set), mode: "custom" });
|
|
136
|
+
};
|
|
137
|
+
const set_approval = (name, mode_value) => {
|
|
138
|
+
if (disabled)
|
|
139
|
+
return;
|
|
140
|
+
const next = { ...(props.value?.approval || {}) };
|
|
141
|
+
next[name] = mode_value;
|
|
142
|
+
on_change({ ...props.value, approval: next });
|
|
143
|
+
};
|
|
144
|
+
const select_all = () => {
|
|
145
|
+
if (disabled)
|
|
146
|
+
return;
|
|
147
|
+
on_change({ ...props.value, selected: Array.from(all_names), mode: "custom" });
|
|
148
|
+
};
|
|
149
|
+
const select_none = () => {
|
|
150
|
+
if (disabled)
|
|
151
|
+
return;
|
|
152
|
+
on_change({ ...props.value, selected: [], mode: "custom" });
|
|
153
|
+
};
|
|
154
|
+
const selected_count = effective_selected.size;
|
|
155
|
+
return (_jsxs("div", { className: `af-tool-policy ${props.className || ""}`.trim(), children: [_jsxs("div", { className: "af-tool-policy__header", children: [_jsx("div", { className: "af-tool-policy__title", children: title }), _jsx("div", { className: "af-tool-policy__subtitle", children: subtitle }), tool_mode ? (_jsxs("div", { className: `af-tool-policy__mode is-${tool_mode.tone}`, children: [_jsx("div", { className: "af-tool-policy__mode-badge", children: "Tool mode" }), _jsx("div", { className: "af-tool-policy__mode-value", children: tool_mode.label }), _jsx("div", { className: "af-tool-policy__mode-detail", children: tool_mode.detail })] })) : null, note ? _jsx("div", { className: "af-tool-policy__note", children: note }) : null] }), _jsxs("div", { className: "af-tool-policy__controls", children: [_jsxs("div", { className: "af-tool-policy__segmented", role: "tablist", "aria-label": "Tool allowlist mode", children: [_jsx("button", { type: "button", className: `af-tool-policy__seg-btn ${mode === "all" ? "is-active" : ""}`.trim(), onClick: () => toggle_mode("all"), disabled: disabled, children: "All tools" }), _jsx("button", { type: "button", className: `af-tool-policy__seg-btn ${mode === "custom" ? "is-active" : ""}`.trim(), onClick: () => toggle_mode("custom"), disabled: disabled, children: "Custom allowlist" })] }), _jsxs("div", { className: "af-tool-policy__count", children: [selected_count, " / ", all_names.length, " enabled"] }), _jsxs("div", { className: "af-tool-policy__bulk", children: [_jsx("button", { type: "button", onClick: select_all, disabled: disabled || mode !== "custom", children: "Select all" }), _jsx("button", { type: "button", onClick: select_none, disabled: disabled || mode !== "custom", children: "Select none" })] })] }), _jsx("div", { className: "af-tool-policy__filter", children: _jsx("input", { type: "text", placeholder: "Filter tools...", value: filter, onChange: (e) => setFilter(e.target.value), disabled: disabled }) }), _jsxs("div", { className: "af-tool-policy__list", children: [filtered.map((tool) => {
|
|
156
|
+
const is_checked = effective_selected.has(tool.name);
|
|
157
|
+
const approval = props.value?.approval?.[tool.name] || default_mode_for(tool.name, defaults);
|
|
158
|
+
return (_jsxs("div", { className: `af-tool-row ${is_checked ? "is-enabled" : ""}`.trim(), children: [_jsx("label", { className: "af-tool-row__check", children: _jsx("input", { type: "checkbox", checked: is_checked, disabled: disabled || mode !== "custom", onChange: (e) => toggle_tool(tool.name, e.target.checked) }) }), _jsxs("div", { className: "af-tool-row__meta", children: [_jsxs("div", { className: "af-tool-row__title", children: [_jsx("span", { className: "af-tool-row__name", children: tool.name }), tool.toolset ? _jsx("span", { className: "af-tool-row__badge", children: tool.toolset }) : null] }), tool.description ? _jsx("div", { className: "af-tool-row__desc", children: tool.description }) : null, tool.when_to_use ? _jsx("div", { className: "af-tool-row__hint", children: tool.when_to_use }) : null] }), _jsx("div", { className: "af-tool-row__approval", children: _jsxs("select", { value: approval, disabled: disabled || !is_checked, onChange: (e) => set_approval(tool.name, e.target.value), children: [_jsx("option", { value: "approve", children: "Approve" }), _jsx("option", { value: "ask", children: "Ask" })] }) })] }, tool.name));
|
|
159
|
+
}), !filtered.length ? _jsx("div", { className: "af-tool-policy__empty", children: "No tools match the filter." }) : null] })] }));
|
|
160
|
+
}
|
|
161
|
+
export default ToolPolicyEditor;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { type FontScaleOption, type HeaderDensityOption } from "./typography";
|
|
2
|
+
import { type FontScaleOption, type HeaderDensityOption } from "./typography.js";
|
|
3
3
|
export type FontScaleSelectProps = {
|
|
4
4
|
value: string;
|
|
5
5
|
onChange: (font_scale_id: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typography_select.d.ts","sourceRoot":"","sources":["../src/typography_select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAiC,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"typography_select.d.ts","sourceRoot":"","sources":["../src/typography_select.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,EAAiC,KAAK,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEhH,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,MAAM,CAAC,EAAE,eAAe,EAAE,CAAC;IAC3B,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;CACtB,CAAC;AAEF,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CAuB/E;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,SAAS,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAClC,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;CACtB,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAuBvF;AAED,eAAe,eAAe,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
|
-
import { AfSelect } from "./af_select";
|
|
4
|
-
import { FONT_SCALES, HEADER_DENSITIES } from "./typography";
|
|
3
|
+
import { AfSelect } from "./af_select.js";
|
|
4
|
+
import { FONT_SCALES, HEADER_DENSITIES } from "./typography.js";
|
|
5
5
|
export function FontScaleSelect(props) {
|
|
6
6
|
const scales = props.scales && props.scales.length ? props.scales : FONT_SCALES;
|
|
7
7
|
const variant = props.variant || "panel";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abstractframework/ui-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Shared UI tokens + themes for AbstractFramework thin clients (AbstractFlow, AbstractObserver, etc.).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Laurent-Philippe Albou",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"react-dom": "^18.0.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"react": "^18.0.0",
|
|
48
|
-
"react-dom": "^18.0.0",
|
|
49
47
|
"@types/react": "^18.2.48",
|
|
50
48
|
"@types/react-dom": "^18.2.18",
|
|
49
|
+
"react": "^18.0.0",
|
|
50
|
+
"react-dom": "^18.0.0",
|
|
51
51
|
"typescript": "^5.3.3"
|
|
52
52
|
},
|
|
53
53
|
"sideEffects": [
|
package/src/theme.css
CHANGED
|
@@ -201,6 +201,255 @@
|
|
|
201
201
|
--border-accent: rgba(136, 192, 208, 0.55);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/* Tool policy editor (allowlist + approve/ask) */
|
|
205
|
+
.af-tool-policy {
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-direction: column;
|
|
208
|
+
gap: 12px;
|
|
209
|
+
padding: 16px;
|
|
210
|
+
border-radius: var(--radius-lg);
|
|
211
|
+
border: 1px solid var(--ui-border-1);
|
|
212
|
+
background: var(--ui-surface-1);
|
|
213
|
+
box-shadow: var(--ui-shadow-1);
|
|
214
|
+
color: var(--text-primary);
|
|
215
|
+
font-family: var(--font-sans);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.af-tool-policy__header {
|
|
219
|
+
display: flex;
|
|
220
|
+
flex-direction: column;
|
|
221
|
+
gap: 6px;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.af-tool-policy__title {
|
|
225
|
+
font-size: var(--font-size-xs);
|
|
226
|
+
font-weight: 700;
|
|
227
|
+
letter-spacing: 0.12em;
|
|
228
|
+
color: var(--accent);
|
|
229
|
+
text-transform: uppercase;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.af-tool-policy__subtitle {
|
|
233
|
+
font-size: var(--font-size-sm);
|
|
234
|
+
color: var(--text-secondary);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.af-tool-policy__mode {
|
|
238
|
+
display: grid;
|
|
239
|
+
grid-template-columns: auto auto 1fr;
|
|
240
|
+
gap: 6px 10px;
|
|
241
|
+
align-items: center;
|
|
242
|
+
padding: 8px 10px;
|
|
243
|
+
border-radius: var(--radius-md);
|
|
244
|
+
border: 1px solid var(--ui-border-1);
|
|
245
|
+
background: var(--ui-overlay-bg);
|
|
246
|
+
font-size: var(--font-size-xs);
|
|
247
|
+
font-weight: 600;
|
|
248
|
+
color: var(--text-primary);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.af-tool-policy__mode-badge {
|
|
252
|
+
text-transform: uppercase;
|
|
253
|
+
letter-spacing: 0.08em;
|
|
254
|
+
font-size: var(--font-size-xxs);
|
|
255
|
+
color: var(--text-muted);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.af-tool-policy__mode-value {
|
|
259
|
+
font-weight: 800;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.af-tool-policy__mode-detail {
|
|
263
|
+
grid-column: 1 / -1;
|
|
264
|
+
font-size: var(--font-size-xs);
|
|
265
|
+
font-weight: 500;
|
|
266
|
+
color: var(--text-secondary);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.af-tool-policy__mode.is-safe {
|
|
270
|
+
border-color: color-mix(in srgb, var(--accent) 50%, transparent);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
.af-tool-policy__mode.is-warn {
|
|
274
|
+
border-color: color-mix(in srgb, #f59e0b 50%, transparent);
|
|
275
|
+
background: color-mix(in srgb, #f59e0b 12%, var(--ui-overlay-bg));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.af-tool-policy__mode.is-danger {
|
|
279
|
+
border-color: color-mix(in srgb, #ef4444 55%, transparent);
|
|
280
|
+
background: color-mix(in srgb, #ef4444 14%, var(--ui-overlay-bg));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.af-tool-policy__mode.is-info {
|
|
284
|
+
border-color: color-mix(in srgb, #38bdf8 50%, transparent);
|
|
285
|
+
background: color-mix(in srgb, #38bdf8 12%, var(--ui-overlay-bg));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.af-tool-policy__note {
|
|
289
|
+
font-size: var(--font-size-xs);
|
|
290
|
+
color: var(--text-muted);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.af-tool-policy__controls {
|
|
294
|
+
display: grid;
|
|
295
|
+
grid-template-columns: minmax(180px, 1fr) auto auto;
|
|
296
|
+
gap: 10px;
|
|
297
|
+
align-items: center;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.af-tool-policy__segmented {
|
|
301
|
+
display: inline-flex;
|
|
302
|
+
background: var(--ui-overlay-bg);
|
|
303
|
+
border: 1px solid var(--ui-overlay-border);
|
|
304
|
+
border-radius: var(--radius-lg);
|
|
305
|
+
padding: 2px;
|
|
306
|
+
gap: 2px;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.af-tool-policy__seg-btn {
|
|
310
|
+
border: none;
|
|
311
|
+
background: transparent;
|
|
312
|
+
color: var(--text-secondary);
|
|
313
|
+
padding: 6px 12px;
|
|
314
|
+
font-size: var(--font-size-sm);
|
|
315
|
+
font-weight: 600;
|
|
316
|
+
border-radius: var(--radius-md);
|
|
317
|
+
cursor: pointer;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.af-tool-policy__seg-btn.is-active {
|
|
321
|
+
background: var(--ui-surface-2);
|
|
322
|
+
color: var(--text-primary);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
.af-tool-policy__seg-btn:disabled {
|
|
326
|
+
cursor: not-allowed;
|
|
327
|
+
opacity: 0.6;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.af-tool-policy__count {
|
|
331
|
+
padding: 6px 12px;
|
|
332
|
+
border-radius: var(--radius-lg);
|
|
333
|
+
border: 1px solid var(--ui-border-1);
|
|
334
|
+
background: var(--ui-overlay-bg);
|
|
335
|
+
color: var(--text-secondary);
|
|
336
|
+
font-size: var(--font-size-xs);
|
|
337
|
+
font-weight: 600;
|
|
338
|
+
text-align: center;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
.af-tool-policy__bulk {
|
|
342
|
+
display: inline-flex;
|
|
343
|
+
gap: 6px;
|
|
344
|
+
justify-self: end;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.af-tool-policy__bulk button {
|
|
348
|
+
border: 1px solid var(--ui-border-1);
|
|
349
|
+
background: var(--ui-overlay-bg);
|
|
350
|
+
color: var(--text-secondary);
|
|
351
|
+
border-radius: var(--radius-md);
|
|
352
|
+
padding: 6px 10px;
|
|
353
|
+
font-size: var(--font-size-xs);
|
|
354
|
+
font-weight: 600;
|
|
355
|
+
cursor: pointer;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.af-tool-policy__bulk button:disabled {
|
|
359
|
+
cursor: not-allowed;
|
|
360
|
+
opacity: 0.5;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.af-tool-policy__filter input {
|
|
364
|
+
width: 100%;
|
|
365
|
+
border-radius: var(--radius-md);
|
|
366
|
+
border: 1px solid var(--ui-border-1);
|
|
367
|
+
background: var(--ui-overlay-bg);
|
|
368
|
+
color: var(--text-primary);
|
|
369
|
+
padding: 8px 10px;
|
|
370
|
+
font-size: var(--font-size-sm);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.af-tool-policy__list {
|
|
374
|
+
display: flex;
|
|
375
|
+
flex-direction: column;
|
|
376
|
+
gap: 10px;
|
|
377
|
+
max-height: 420px;
|
|
378
|
+
overflow: auto;
|
|
379
|
+
padding-right: 4px;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.af-tool-row {
|
|
383
|
+
display: grid;
|
|
384
|
+
grid-template-columns: 24px 1fr auto;
|
|
385
|
+
gap: 10px;
|
|
386
|
+
align-items: flex-start;
|
|
387
|
+
padding: 10px 12px;
|
|
388
|
+
border-radius: var(--radius-md);
|
|
389
|
+
border: 1px solid var(--ui-border-1);
|
|
390
|
+
background: var(--ui-surface-2);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.af-tool-row.is-enabled {
|
|
394
|
+
border-color: var(--accent-border);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.af-tool-row__check input {
|
|
398
|
+
width: 16px;
|
|
399
|
+
height: 16px;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.af-tool-row__title {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: center;
|
|
405
|
+
gap: 8px;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.af-tool-row__name {
|
|
409
|
+
font-weight: 700;
|
|
410
|
+
font-size: var(--font-size-sm);
|
|
411
|
+
color: var(--text-primary);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
.af-tool-row__badge {
|
|
415
|
+
padding: 2px 8px;
|
|
416
|
+
border-radius: var(--radius-md);
|
|
417
|
+
border: 1px solid var(--ui-chip-border);
|
|
418
|
+
background: var(--ui-chip-bg);
|
|
419
|
+
color: var(--ui-chip-text);
|
|
420
|
+
font-size: var(--font-size-xxs);
|
|
421
|
+
text-transform: uppercase;
|
|
422
|
+
letter-spacing: 0.08em;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.af-tool-row__desc {
|
|
426
|
+
font-size: var(--font-size-xs);
|
|
427
|
+
color: var(--text-secondary);
|
|
428
|
+
margin-top: 4px;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
.af-tool-row__hint {
|
|
432
|
+
font-size: var(--font-size-xxs);
|
|
433
|
+
color: var(--text-muted);
|
|
434
|
+
margin-top: 4px;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.af-tool-row__approval select {
|
|
438
|
+
border-radius: var(--radius-md);
|
|
439
|
+
border: 1px solid var(--ui-border-1);
|
|
440
|
+
background: var(--ui-overlay-bg);
|
|
441
|
+
color: var(--text-primary);
|
|
442
|
+
padding: 6px 8px;
|
|
443
|
+
font-size: var(--font-size-xs);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
.af-tool-policy__empty {
|
|
447
|
+
padding: 18px;
|
|
448
|
+
text-align: center;
|
|
449
|
+
color: var(--text-muted);
|
|
450
|
+
font-size: var(--font-size-sm);
|
|
451
|
+
}
|
|
452
|
+
|
|
204
453
|
:root.theme-dracula {
|
|
205
454
|
--bg-primary: #282a36;
|
|
206
455
|
--bg-secondary: #343746;
|
|
@@ -885,6 +1134,13 @@
|
|
|
885
1134
|
font-size: var(--font-size-base);
|
|
886
1135
|
}
|
|
887
1136
|
|
|
1137
|
+
.af-select-custom-error {
|
|
1138
|
+
margin-top: 6px;
|
|
1139
|
+
color: var(--accent);
|
|
1140
|
+
font-size: var(--font-size-xs);
|
|
1141
|
+
line-height: 1.35;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
888
1144
|
.af-select-options {
|
|
889
1145
|
max-height: 340px;
|
|
890
1146
|
overflow: auto;
|
|
@@ -926,13 +1182,37 @@
|
|
|
926
1182
|
background: var(--ui-surface-2, rgba(255, 255, 255, 0.06));
|
|
927
1183
|
}
|
|
928
1184
|
|
|
1185
|
+
.af-select-option--disabled {
|
|
1186
|
+
cursor: not-allowed;
|
|
1187
|
+
color: var(--text-muted);
|
|
1188
|
+
opacity: 0.58;
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.af-select-option--disabled.af-select-option--highlighted {
|
|
1192
|
+
background: var(--ui-surface-1, rgba(255, 255, 255, 0.04));
|
|
1193
|
+
}
|
|
1194
|
+
|
|
929
1195
|
.af-select-option-label {
|
|
1196
|
+
display: flex;
|
|
1197
|
+
flex-direction: column;
|
|
1198
|
+
gap: 2px;
|
|
930
1199
|
flex: 1;
|
|
1200
|
+
min-width: 0;
|
|
1201
|
+
overflow: hidden;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
.af-select-option-label > span {
|
|
931
1205
|
overflow: hidden;
|
|
932
1206
|
text-overflow: ellipsis;
|
|
933
1207
|
white-space: nowrap;
|
|
934
1208
|
}
|
|
935
1209
|
|
|
1210
|
+
.af-select-option-reason {
|
|
1211
|
+
color: var(--text-muted);
|
|
1212
|
+
font-size: var(--font-size-xs);
|
|
1213
|
+
line-height: 1.25;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
936
1216
|
.af-select-check {
|
|
937
1217
|
color: var(--accent);
|
|
938
1218
|
font-weight: 700;
|