devnonla-ui 0.1.0 → 0.2.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 +22 -10
- package/package.json +2 -2
- package/src/alert/Alert.tsx +1 -1
- package/src/app/App.tsx +38 -18
- package/src/app/context.ts +32 -0
- package/src/button/Button.tsx +2 -2
- package/src/calendar/Calendar.tsx +4 -4
- package/src/chat/AgentPanel.tsx +15 -6
- package/src/chat/common/types.ts +27 -2
- package/src/chat/common/useAgentStream.ts +78 -12
- package/src/chat/common/utils.ts +13 -0
- package/src/chat/index.ts +5 -3
- package/src/chat/message-ui/ChatAgentMessage.tsx +1 -1
- package/src/chat/message-ui/ChatInput.tsx +1 -1
- package/src/chat/message-ui/ChatMarkdown.tsx +6 -6
- package/src/chat/message-ui/ChatMarkdownTable.tsx +1 -1
- package/src/chat/message-ui/ChatThinking.tsx +4 -3
- package/src/chat/message-ui/ChatUserMessage.tsx +0 -8
- package/src/chat/message-ui/MermaidBlock.tsx +11 -1
- package/src/chat/tool-ui/BackgroundTaskToolUI.tsx +2 -1
- package/src/chat/tool-ui/CallAgentToolUI.tsx +3 -2
- package/src/chat/tool-ui/ChatToolCall.tsx +3 -2
- package/src/chat/tool-ui/GetCurrentTimeToolUI.tsx +2 -1
- package/src/chat/tool-ui/ReadSkillToolUI.tsx +3 -2
- package/src/chat/tool-ui/RunJsToolUI.tsx +3 -2
- package/src/chat/tool-ui/WebFetchToolUI.tsx +3 -2
- package/src/chat/tool-ui/registry.ts +24 -12
- package/src/checkbox/Checkbox.tsx +5 -4
- package/src/codeblock/CodeBlock.tsx +1 -0
- package/src/datepicker/DatePicker.tsx +15 -9
- package/src/desktop/DesktopHeader.tsx +1 -1
- package/src/desktop/DesktopIcon.tsx +2 -4
- package/src/desktop/DesktopWindow.tsx +19 -5
- package/src/desktop/MeadowDesktop.tsx +2 -2
- package/src/drawer/Drawer.tsx +8 -6
- package/src/dropdown/ContextMenu.tsx +3 -1
- package/src/dropdown/Dropdown.tsx +8 -6
- package/src/form/Form.tsx +2 -2
- package/src/form/FormItem.tsx +2 -2
- package/src/form/partials/FormItemHelps.tsx +2 -2
- package/src/form/variants/FieldColor.tsx +1 -1
- package/src/form/variants/FieldObject.tsx +2 -2
- package/src/form/variants/FieldRepeaterItem.tsx +4 -4
- package/src/form-layout/FormLayout.tsx +6 -6
- package/src/index.ts +20 -6
- package/src/input/Input.tsx +11 -10
- package/src/lib/sizes.ts +13 -3
- package/src/lib/surface.ts +2 -1
- package/src/menu/Menu.tsx +2 -2
- package/src/message/message.tsx +35 -2
- package/src/modal/Modal.tsx +49 -3
- package/src/pagination/Pagination.tsx +6 -5
- package/src/popconfirm/Popconfirm.tsx +1 -1
- package/src/popover/Popover.tsx +6 -8
- package/src/scroll/OverlayScroll.tsx +5 -3
- package/src/segmented/Segmented.tsx +6 -5
- package/src/select/Select.tsx +9 -6
- package/src/shimmer/Shimmer.tsx +17 -0
- package/src/skeleton/Skeleton.tsx +3 -3
- package/src/spin/Spin.tsx +8 -5
- package/src/styles.css +81 -27
- package/src/switch/Switch.tsx +6 -5
- package/src/table/Table.tsx +6 -7
- package/src/tag/Tag.tsx +2 -2
- package/src/theme.ts +23 -3
- package/src/timepicker/TimePicker.tsx +13 -11
- package/src/tooltip/Tooltip.tsx +6 -7
package/src/theme.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Theme knobs for NonlaUI.
|
|
3
3
|
*
|
|
4
|
-
* Defaults live in `styles.css` on `:root` / `.
|
|
4
|
+
* Defaults live in `styles.css` on `:root` / `.nonla-ui`.
|
|
5
5
|
* `--brand-50`…`--brand-800` follow `--nonla-brand` (500 = the knob).
|
|
6
6
|
* Consumers retheme without touching components:
|
|
7
7
|
* 1. CSS: `:root { --nonla-brand: #3b82f6; }`
|
|
@@ -43,6 +43,11 @@ export const NONLA_THEME_KEYS = {
|
|
|
43
43
|
inkActive: "--nonla-ink-active",
|
|
44
44
|
inkLine: "--nonla-ink-line",
|
|
45
45
|
desktopBarHeight: "--nonla-desktop-bar-height",
|
|
46
|
+
zBase: "--nonla-z-base",
|
|
47
|
+
zDesktop: "--nonla-z-desktop",
|
|
48
|
+
zWindow: "--nonla-z-window",
|
|
49
|
+
zHeader: "--nonla-z-header",
|
|
50
|
+
zPopupBase: "--nonla-z-popup-base",
|
|
46
51
|
blue: "--nonla-blue",
|
|
47
52
|
purple: "--nonla-purple",
|
|
48
53
|
cyan: "--nonla-cyan",
|
|
@@ -61,9 +66,12 @@ export const NONLA_THEME_KEYS = {
|
|
|
61
66
|
export type NonlaThemeKnobName = keyof typeof NONLA_THEME_KEYS;
|
|
62
67
|
export type NonlaThemeKnob = (typeof NONLA_THEME_KEYS)[NonlaThemeKnobName];
|
|
63
68
|
|
|
64
|
-
const METRIC_KEYS = new Set<NonlaThemeKnobName>(["radius", "height", "heightSm", "heightLg", "desktopBarHeight"]);
|
|
69
|
+
const METRIC_KEYS = new Set<NonlaThemeKnobName>(["radius", "height", "heightSm", "heightLg", "desktopBarHeight", "zBase", "zDesktop", "zWindow", "zHeader", "zPopupBase"]);
|
|
65
70
|
|
|
66
|
-
export type NonlaThemeColorName = Exclude<
|
|
71
|
+
export type NonlaThemeColorName = Exclude<
|
|
72
|
+
NonlaThemeKnobName,
|
|
73
|
+
"radius" | "height" | "heightSm" | "heightLg" | "desktopBarHeight" | "zBase" | "zDesktop" | "zWindow" | "zHeader" | "zPopupBase"
|
|
74
|
+
>;
|
|
67
75
|
export type NonlaThemeColors = { [K in NonlaThemeColorName]?: string };
|
|
68
76
|
|
|
69
77
|
/** Flat aliases → knob name (`colorBorder` == `colors.border`). */
|
|
@@ -117,6 +125,18 @@ function entriesOf(theme: NonlaThemeConfig): [string, string][] {
|
|
|
117
125
|
return [...map.entries()];
|
|
118
126
|
}
|
|
119
127
|
|
|
128
|
+
export type NonlaTokenSnapshot = { [K in NonlaThemeKnobName]: string };
|
|
129
|
+
|
|
130
|
+
/** Read computed `--nonla-*` knobs (`getDesignToken`). */
|
|
131
|
+
export function getDesignToken(target?: HTMLElement): NonlaTokenSnapshot {
|
|
132
|
+
const el = target ?? (typeof document === "undefined" ? null : document.documentElement);
|
|
133
|
+
const token = {} as NonlaTokenSnapshot;
|
|
134
|
+
for (const name of Object.keys(NONLA_THEME_KEYS) as NonlaThemeKnobName[]) {
|
|
135
|
+
token[name] = el ? getComputedStyle(el).getPropertyValue(NONLA_THEME_KEYS[name]).trim() : "";
|
|
136
|
+
}
|
|
137
|
+
return token;
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
/** Apply knobs on an element (defaults to `:root`). Returns a restore function. */
|
|
121
141
|
export function applyNonlaTheme(theme: NonlaThemeConfig, target: HTMLElement = document.documentElement): () => void {
|
|
122
142
|
const entries = entriesOf(theme);
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
useRef,
|
|
13
13
|
useState,
|
|
14
14
|
} from "react";
|
|
15
|
-
import {
|
|
15
|
+
import { usePopupContainer } from "../app/context";
|
|
16
16
|
import { cn } from "../lib/cn";
|
|
17
17
|
import { type PopperPlacement, placementToRadix } from "../lib/placement";
|
|
18
|
-
import { type ControlSize, controlFieldFocusBorder, controlFieldStyle, controlFieldSurface, controlFieldTransition, controlStatusClass, getSizeTokens } from "../lib/sizes";
|
|
18
|
+
import { type ControlSize, controlFieldFocusBorder, controlFieldStyle, controlFieldSurface, controlFieldTransition, controlStatusClass, getSizeTokens, useControlSize } from "../lib/sizes";
|
|
19
19
|
import { glassOverlayClass } from "../lib/surface";
|
|
20
20
|
|
|
21
21
|
export type TimeValue = { hours: number; minutes: number; seconds: number };
|
|
@@ -244,7 +244,7 @@ function StepperButton({
|
|
|
244
244
|
tabIndex={-1}
|
|
245
245
|
aria-label={label}
|
|
246
246
|
className={cn(
|
|
247
|
-
"flex h-10 items-center justify-center rounded-
|
|
247
|
+
"flex h-10 items-center justify-center rounded-md text-muted-foreground transition-colors",
|
|
248
248
|
"hover:bg-foreground/5 hover:text-foreground active:bg-foreground/8",
|
|
249
249
|
)}
|
|
250
250
|
style={{ width: size }}
|
|
@@ -370,7 +370,7 @@ function StepperBoard({
|
|
|
370
370
|
tabIndex={-1}
|
|
371
371
|
aria-label={`Choose ${col.label}`}
|
|
372
372
|
className={cn(
|
|
373
|
-
"flex cursor-pointer items-center justify-center rounded-
|
|
373
|
+
"flex cursor-pointer items-center justify-center rounded-md font-semibold leading-none tracking-tight transition-colors",
|
|
374
374
|
col.wide ? "text-[23px]" : "tabular-nums",
|
|
375
375
|
col.active ? "bg-foreground/6 text-foreground" : "text-foreground hover:bg-foreground/5",
|
|
376
376
|
)}
|
|
@@ -414,7 +414,7 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
414
414
|
},
|
|
415
415
|
ref,
|
|
416
416
|
) {
|
|
417
|
-
const
|
|
417
|
+
const appPortal = usePopupContainer();
|
|
418
418
|
const showSeconds = format === "HH:mm:ss";
|
|
419
419
|
const controlled = value !== undefined;
|
|
420
420
|
const [inner, setInner] = useState<TimeValue | null>(() => parseTime(defaultValue));
|
|
@@ -428,8 +428,9 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
428
428
|
const [fallback, setFallback] = useState<TimeValue>(nowParts);
|
|
429
429
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
|
430
430
|
const fieldRef = useRef<HTMLDivElement | null>(null);
|
|
431
|
-
const
|
|
432
|
-
const
|
|
431
|
+
const resolvedSize = useControlSize(size);
|
|
432
|
+
const tok = getSizeTokens(resolvedSize);
|
|
433
|
+
const fieldStyle = controlFieldStyle(resolvedSize);
|
|
433
434
|
const { side, align } = placementToRadix(placement);
|
|
434
435
|
|
|
435
436
|
useEffect(() => {
|
|
@@ -593,6 +594,7 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
593
594
|
<span className="inline-flex shrink-0 text-muted-foreground">
|
|
594
595
|
<ClockIcon size={tok.icon} />
|
|
595
596
|
</span>
|
|
597
|
+
{/* biome-ignore lint/a11y/useAriaPropsSupportedByRole: native time field exposes popup state */}
|
|
596
598
|
<input
|
|
597
599
|
ref={setRefs}
|
|
598
600
|
id={id}
|
|
@@ -647,7 +649,7 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
647
649
|
) : null}
|
|
648
650
|
</div>
|
|
649
651
|
</PopoverPrimitive.Anchor>
|
|
650
|
-
<PopoverPrimitive.Portal container={
|
|
652
|
+
<PopoverPrimitive.Portal container={appPortal?.()}>
|
|
651
653
|
<PopoverPrimitive.Content
|
|
652
654
|
side={side}
|
|
653
655
|
align={align}
|
|
@@ -668,14 +670,14 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
668
670
|
if (fieldRef.current?.contains(e.target as Node)) e.preventDefault();
|
|
669
671
|
}}
|
|
670
672
|
className={cn(
|
|
671
|
-
"w-
|
|
673
|
+
"w-75 p-0",
|
|
672
674
|
glassOverlayClass,
|
|
673
675
|
popupClassName,
|
|
674
676
|
)}
|
|
675
677
|
>
|
|
676
678
|
<div className="relative overflow-hidden rounded-[inherit]">
|
|
677
679
|
{picking && pickOptions ? (
|
|
678
|
-
<div className="relative flex h-
|
|
680
|
+
<div className="relative flex h-55 items-center px-3 pt-8 pb-3">
|
|
679
681
|
<button
|
|
680
682
|
type="button"
|
|
681
683
|
tabIndex={-1}
|
|
@@ -690,7 +692,7 @@ export const TimePicker = forwardRef<HTMLInputElement, TimePickerProps>(function
|
|
|
690
692
|
</div>
|
|
691
693
|
</div>
|
|
692
694
|
) : (
|
|
693
|
-
<div className="flex h-
|
|
695
|
+
<div className="flex h-55 flex-col items-center justify-center px-4">
|
|
694
696
|
<StepperBoard
|
|
695
697
|
columns={[
|
|
696
698
|
{
|
package/src/tooltip/Tooltip.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import { usePopupContainer } from "../app/context";
|
|
4
4
|
import { cn } from "../lib/cn";
|
|
5
5
|
import { type PopperPlacement, placementToRadix } from "../lib/placement";
|
|
6
6
|
import { glassOverlayClass } from "../lib/surface";
|
|
@@ -16,10 +16,10 @@ export type TooltipProps = {
|
|
|
16
16
|
onOpenChange?: (open: boolean) => void;
|
|
17
17
|
mouseEnterDelay?: number;
|
|
18
18
|
className?: string;
|
|
19
|
-
/**
|
|
19
|
+
/** Extra class on the overlay panel. */
|
|
20
20
|
overlayClassName?: string;
|
|
21
21
|
getPopupContainer?: () => HTMLElement;
|
|
22
|
-
/**
|
|
22
|
+
/** `boolean | { pointAtCenter }` — we only honor truthiness. */
|
|
23
23
|
arrow?: boolean | { pointAtCenter?: boolean };
|
|
24
24
|
};
|
|
25
25
|
|
|
@@ -35,15 +35,14 @@ export function Tooltip({
|
|
|
35
35
|
overlayClassName,
|
|
36
36
|
getPopupContainer,
|
|
37
37
|
}: TooltipProps) {
|
|
38
|
-
const
|
|
38
|
+
const getContainer = usePopupContainer(getPopupContainer);
|
|
39
39
|
const { side, align } = placementToRadix(placement);
|
|
40
|
-
const container = getPopupContainer ?? app.getPopupContainer;
|
|
41
40
|
if (title == null || title === false) return <>{children}</>;
|
|
42
41
|
return (
|
|
43
42
|
<TooltipPrimitive.Provider delayDuration={Math.round(mouseEnterDelay * 1000)}>
|
|
44
43
|
<TooltipPrimitive.Root open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>
|
|
45
44
|
<TooltipPrimitive.Trigger asChild>{children}</TooltipPrimitive.Trigger>
|
|
46
|
-
<TooltipPrimitive.Portal container={
|
|
45
|
+
<TooltipPrimitive.Portal container={getContainer()}>
|
|
47
46
|
<TooltipPrimitive.Content
|
|
48
47
|
side={side}
|
|
49
48
|
align={align}
|