@annondeveloper/ui-kit 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2DWZVHZS.js → chunk-H3UQODJH.js} +20 -4
- package/dist/chunk-H3UQODJH.js.map +1 -0
- package/dist/form.js +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/src/components/streaming-text.tsx +2 -0
- package/src/utils.ts +33 -0
- package/dist/chunk-2DWZVHZS.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@annondeveloper/ui-kit",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A React component library with dark/light theming, built on Radix UI, Tailwind CSS v4, and Framer Motion.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -108,5 +108,8 @@
|
|
|
108
108
|
"design-system",
|
|
109
109
|
"typescript"
|
|
110
110
|
],
|
|
111
|
-
"sideEffects": false
|
|
111
|
+
"sideEffects": false,
|
|
112
|
+
"dependencies": {
|
|
113
|
+
"isomorphic-dompurify": "^3.5.1"
|
|
114
|
+
}
|
|
112
115
|
}
|
|
@@ -23,6 +23,8 @@ export interface StreamingTextProps {
|
|
|
23
23
|
/** Simple inline formatter: **bold** and `code` spans. */
|
|
24
24
|
function formatSegments(text: string): React.ReactNode[] {
|
|
25
25
|
const segments: React.ReactNode[] = []
|
|
26
|
+
// Sanitize input before processing — React escapes text nodes by default,
|
|
27
|
+
// but we sanitize to strip any unexpected control characters or injection attempts
|
|
26
28
|
const regex = /(\*\*(.+?)\*\*|`([^`]+?)`)/g
|
|
27
29
|
let lastIndex = 0
|
|
28
30
|
let match: RegExpExecArray | null
|
package/src/utils.ts
CHANGED
|
@@ -121,3 +121,36 @@ export function utilColor(pct: number, colorMap: UtilColorMap = defaultUtilColor
|
|
|
121
121
|
if (pct >= colorMap.medium.threshold) return colorMap.medium.className
|
|
122
122
|
return colorMap.low.className
|
|
123
123
|
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Sanitize a string for safe rendering. Uses DOMPurify when available,
|
|
127
|
+
* falls back to basic HTML entity escaping.
|
|
128
|
+
* Import `isomorphic-dompurify` as an optional peer dependency for full sanitization.
|
|
129
|
+
*/
|
|
130
|
+
export function sanitize(input: string): string {
|
|
131
|
+
try {
|
|
132
|
+
// Dynamic import to keep DOMPurify optional
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
134
|
+
const DOMPurify = require('isomorphic-dompurify')
|
|
135
|
+
return DOMPurify.sanitize(input, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }) as string
|
|
136
|
+
} catch {
|
|
137
|
+
// Fallback: strip all HTML tags and escape entities
|
|
138
|
+
return input
|
|
139
|
+
.replace(/&/g, '&')
|
|
140
|
+
.replace(/</g, '<')
|
|
141
|
+
.replace(/>/g, '>')
|
|
142
|
+
.replace(/"/g, '"')
|
|
143
|
+
.replace(/'/g, ''')
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validate that a string is a safe CSS color value (hex, rgb, hsl).
|
|
149
|
+
* Rejects CSS injection attempts like `expression()` or `url()`.
|
|
150
|
+
*/
|
|
151
|
+
export function isSafeColor(c: string): boolean {
|
|
152
|
+
return /^#[0-9a-f]{3,8}$/i.test(c) ||
|
|
153
|
+
/^rgba?\(\s*[\d.]+/.test(c) ||
|
|
154
|
+
/^hsla?\(\s*[\d.]+/.test(c) ||
|
|
155
|
+
/^hsl\(var\(--[a-z-]+\)\)$/.test(c)
|
|
156
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/utils.ts","../src/components/form-input.tsx","../src/components/select.tsx","../src/components/checkbox.tsx","../src/components/toggle-switch.tsx"],"names":["jsxs","jsx","Check"],"mappings":"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","file":"chunk-2DWZVHZS.js","sourcesContent":["import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\n/** Merge Tailwind CSS class names with conflict resolution. */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs))\n}\n\n/** Format bits/sec into human-readable string (clamps negative to 0). */\nexport function fmtBps(bps: number): string {\n const v = Math.max(0, bps)\n if (v >= 1e12) return `${(v / 1e12).toFixed(1)} Tbps`\n if (v >= 1e9) return `${(v / 1e9).toFixed(1)} Gbps`\n if (v >= 1e6) return `${(v / 1e6).toFixed(1)} Mbps`\n if (v >= 1e3) return `${(v / 1e3).toFixed(1)} Kbps`\n return `${v.toFixed(0)} bps`\n}\n\n/** Format link speed for interface display (e.g. 1000000000 -> \"1G\"). */\nexport function fmtSpeed(bps: number): string {\n if (bps >= 1e12) return `${(bps / 1e12).toFixed(0)}T`\n if (bps >= 1e9) return `${(bps / 1e9).toFixed(0)}G`\n if (bps >= 1e6) return `${(bps / 1e6).toFixed(0)}M`\n if (bps >= 1e3) return `${(bps / 1e3).toFixed(0)}K`\n return `${bps.toFixed(0)}`\n}\n\n/** Format utilization as percentage of link speed. */\nexport function fmtUtil(bps: number, speedBps: number): string {\n if (speedBps <= 0) return '\\u2014'\n const pct = (Math.max(0, bps) / speedBps) * 100\n if (pct < 0.01) return '0%'\n if (pct < 1) return `${pct.toFixed(2)}%`\n if (pct < 10) return `${pct.toFixed(1)}%`\n return `${pct.toFixed(0)}%`\n}\n\n/** Format bytes into human-readable string. */\nexport function fmtBytes(bytes: number): string {\n if (bytes >= 1e12) return `${(bytes / 1e12).toFixed(1)} TB`\n if (bytes >= 1e9) return `${(bytes / 1e9).toFixed(1)} GB`\n if (bytes >= 1e6) return `${(bytes / 1e6).toFixed(1)} MB`\n if (bytes >= 1e3) return `${(bytes / 1e3).toFixed(1)} KB`\n return `${bytes.toFixed(0)} B`\n}\n\n/** Format percent. */\nexport function fmtPct(v: number, decimals = 1): string {\n return `${v.toFixed(decimals)}%`\n}\n\n/** Format duration in seconds to human-readable uptime string. */\nexport function fmtUptime(secs: number): string {\n if (secs < 60) return `${secs}s`\n if (secs < 3600) return `${Math.floor(secs / 60)}m`\n if (secs < 86400) return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`\n return `${Math.floor(secs / 86400)}d ${Math.floor((secs % 86400) / 3600)}h`\n}\n\n/** Format ISO timestamp to relative time (\"3m ago\"). */\nexport function fmtRelative(isoStr: string): string {\n const diff = (Date.now() - new Date(isoStr).getTime()) / 1000\n if (diff < 60) return 'just now'\n if (diff < 3600) return `${Math.floor(diff / 60)}m ago`\n if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`\n return `${Math.floor(diff / 86400)}d ago`\n}\n\n/** Format large numbers compactly (e.g. 480933305 -> \"480.9M\", 1234 -> \"1.2K\"). */\nexport function fmtCompact(n: number): string {\n const abs = Math.abs(n)\n const sign = n < 0 ? '-' : ''\n if (abs >= 1e12) return `${sign}${(abs / 1e12).toFixed(1)}T`\n if (abs >= 1e9) return `${sign}${(abs / 1e9).toFixed(1)}B`\n if (abs >= 1e6) return `${sign}${(abs / 1e6).toFixed(1)}M`\n if (abs >= 1e3) return `${sign}${(abs / 1e3).toFixed(1)}K`\n if (abs >= 1) return `${sign}${abs.toFixed(0)}`\n if (abs >= 0.01) return `${sign}${abs.toFixed(2)}`\n return `${sign}${abs.toFixed(0)}`\n}\n\n/** Format seconds duration compactly (e.g. for replication lag display). */\nexport function fmtDuration(secs: number): string {\n if (secs < 0.001) return '0s'\n if (secs < 1) return `${(secs * 1000).toFixed(0)}ms`\n if (secs < 60) return `${secs.toFixed(1)}s`\n if (secs < 3600) return `${Math.floor(secs / 60)}m ${Math.floor(secs % 60)}s`\n return `${Math.floor(secs / 3600)}h ${Math.floor((secs % 3600) / 60)}m`\n}\n\n/** Strip CIDR suffix from IP address (e.g. \"10.0.0.1/32\" -> \"10.0.0.1\"). */\nexport function stripCidr(ip: string): string {\n return ip.replace(/\\/\\d+$/, '')\n}\n\n/** Clamp a number between min and max. */\nexport function clamp(v: number, min: number, max: number): number {\n return Math.min(Math.max(v, min), max)\n}\n\n/** Color thresholds for utilization percentage mapping. */\nexport interface UtilColorMap {\n high: { threshold: number; className: string }\n medium: { threshold: number; className: string }\n low: { className: string }\n}\n\n/** Default utilization color map using CSS custom property token classes. */\nexport const defaultUtilColorMap: UtilColorMap = {\n high: { threshold: 80, className: 'text-[hsl(var(--status-critical))]' },\n medium: { threshold: 60, className: 'text-[hsl(var(--status-warning))]' },\n low: { className: 'text-[hsl(var(--status-ok))]' },\n}\n\n/**\n * Get utilization color class from value 0-100.\n * Accepts an optional color map to override the default thresholds and classes.\n */\nexport function utilColor(pct: number, colorMap: UtilColorMap = defaultUtilColorMap): string {\n if (pct >= colorMap.high.threshold) return colorMap.high.className\n if (pct >= colorMap.medium.threshold) return colorMap.medium.className\n return colorMap.low.className\n}\n","'use client'\n\nimport type React from 'react'\nimport { cn } from '../utils'\n\n// ── Shared class constants ──────────────────────────────────────────────────\n// Import these in any page that needs raw class strings (e.g. for <textarea>)\n\nexport const INPUT_CLS: string = cn(\n 'w-full rounded-lg border border-[hsl(var(--border-default))]',\n 'bg-[hsl(var(--bg-base))] px-3 py-2 text-sm',\n 'text-[hsl(var(--text-primary))] placeholder:text-[hsl(var(--text-tertiary))]',\n 'focus:outline-none focus:ring-2 focus:ring-[hsl(var(--brand-primary))]',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n)\n\nexport const LABEL_CLS: string = cn(\n 'mb-1.5 block text-xs font-medium uppercase tracking-wider',\n 'text-[hsl(var(--text-secondary))]',\n)\n\nexport const TEXTAREA_CLS: string = cn(\n INPUT_CLS,\n 'resize-none font-mono text-xs leading-relaxed',\n)\n\nexport interface FormInputProps {\n /** Label text displayed above the input. */\n label: string\n /** Current input value. */\n value: string\n /** Callback when value changes. */\n onChange: (value: string) => void\n /** HTML input type. */\n type?: string\n /** Placeholder text. */\n placeholder?: string\n /** Mark the field as required. */\n required?: boolean\n /** Disable the input. */\n disabled?: boolean\n /** Help text shown below the input. */\n hint?: string\n className?: string\n /** HTML autocomplete attribute. */\n autoComplete?: string\n}\n\n/**\n * @description A themed form input with label, validation indicator, and optional hint text.\n * Uses CSS custom property tokens for dark/light mode compatibility.\n */\nexport function FormInput({\n label, value, onChange, type = 'text',\n placeholder, required, disabled, hint, className, autoComplete,\n}: FormInputProps): React.JSX.Element {\n return (\n <div className={cn('space-y-1.5', className)}>\n <label className={LABEL_CLS}>\n {label}\n {required && <span className=\"text-[hsl(var(--status-critical))] ml-0.5\">*</span>}\n </label>\n <input\n type={type}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n required={required}\n disabled={disabled}\n autoComplete={autoComplete}\n className={INPUT_CLS}\n />\n {hint && (\n <p className=\"text-[10px] text-[hsl(var(--text-tertiary))]\">{hint}</p>\n )}\n </div>\n )\n}\n","'use client'\n\nimport type React from 'react'\nimport * as RadixSelect from '@radix-ui/react-select'\nimport { ChevronDown, Check } from 'lucide-react'\nimport { cn } from '../utils'\n\nexport interface SelectOption {\n value: string\n label: string\n}\n\nexport interface SelectProps {\n /** Currently selected value. */\n value: string\n /** Callback when selection changes. */\n onValueChange: (v: string) => void\n /** Available options. */\n options: SelectOption[]\n /** Placeholder text when no value is selected. */\n placeholder?: string\n className?: string\n /** Disable the select. */\n disabled?: boolean\n}\n\n/**\n * @description A themed select dropdown built on Radix UI Select.\n * Supports dark/light mode via CSS custom property tokens.\n */\nexport function Select({\n value, onValueChange, options, placeholder, className, disabled,\n}: SelectProps): React.JSX.Element {\n return (\n <RadixSelect.Root value={value} onValueChange={onValueChange} disabled={disabled}>\n <RadixSelect.Trigger\n suppressHydrationWarning\n className={cn(\n 'flex w-full items-center justify-between gap-2 rounded-lg',\n 'border border-[hsl(var(--border-default))] bg-[hsl(var(--bg-base))]',\n 'px-3 py-2 text-sm text-[hsl(var(--text-primary))]',\n 'hover:border-[hsl(var(--border-strong))] focus:outline-none',\n 'focus:ring-2 focus:ring-[hsl(var(--brand-primary))]',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n 'data-[placeholder]:text-[hsl(var(--text-tertiary))]',\n className,\n )}\n >\n <RadixSelect.Value placeholder={placeholder} />\n <RadixSelect.Icon>\n <ChevronDown className=\"size-4 text-[hsl(var(--text-tertiary))] shrink-0\" />\n </RadixSelect.Icon>\n </RadixSelect.Trigger>\n\n <RadixSelect.Portal>\n <RadixSelect.Content\n position=\"popper\"\n sideOffset={4}\n className={cn(\n 'z-50 min-w-[var(--radix-select-trigger-width)] overflow-hidden',\n 'rounded-xl border border-[hsl(var(--border-default))]',\n 'bg-[hsl(var(--bg-elevated))] shadow-xl',\n 'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',\n 'data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95',\n )}\n >\n <RadixSelect.Viewport className=\"p-1\">\n {options.map((opt) => (\n <RadixSelect.Item\n key={opt.value}\n value={opt.value}\n className={cn(\n 'relative flex cursor-pointer select-none items-center',\n 'rounded-lg py-2 pl-8 pr-3 text-sm',\n 'text-[hsl(var(--text-primary))]',\n 'outline-none',\n 'hover:bg-[hsl(var(--bg-overlay))]',\n 'focus:bg-[hsl(var(--brand-primary))]/10 focus:text-[hsl(var(--brand-primary))]',\n 'data-[state=checked]:text-[hsl(var(--brand-primary))]',\n )}\n >\n <RadixSelect.ItemIndicator className=\"absolute left-2 flex items-center\">\n <Check className=\"size-4\" />\n </RadixSelect.ItemIndicator>\n <RadixSelect.ItemText>{opt.label}</RadixSelect.ItemText>\n </RadixSelect.Item>\n ))}\n </RadixSelect.Viewport>\n </RadixSelect.Content>\n </RadixSelect.Portal>\n </RadixSelect.Root>\n )\n}\n","'use client'\n\nimport type React from 'react'\nimport { forwardRef, type InputHTMLAttributes } from 'react'\nimport { Check, Minus } from 'lucide-react'\nimport { cn } from '../utils'\n\nexport interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {\n /** Show an indeterminate (minus) indicator instead of a checkmark. */\n indeterminate?: boolean\n /** Size variant. */\n size?: 'sm' | 'md'\n}\n\n/**\n * @description A themed checkbox with indeterminate state support.\n * Uses CSS custom property tokens for dark/light mode compatibility.\n */\nconst Checkbox: React.ForwardRefExoticComponent<CheckboxProps & React.RefAttributes<HTMLInputElement>> = forwardRef<HTMLInputElement, CheckboxProps>(\n ({ indeterminate, checked, size = 'md', className, ...props }, ref): React.JSX.Element => {\n const isChecked = checked || indeterminate\n const sizeClass = size === 'sm' ? 'h-3.5 w-3.5' : 'h-4 w-4'\n const iconSize = size === 'sm' ? 'h-2.5 w-2.5' : 'h-3 w-3'\n\n return (\n <label className={cn('relative inline-flex items-center cursor-pointer', className)}>\n <input\n ref={ref}\n type=\"checkbox\"\n checked={checked}\n className=\"sr-only peer\"\n {...props}\n />\n <div\n className={cn(\n sizeClass,\n 'rounded border transition-colors duration-150',\n 'flex items-center justify-center shrink-0',\n isChecked\n ? 'bg-[hsl(var(--brand-primary))] border-[hsl(var(--brand-primary))]'\n : 'bg-transparent border-[hsl(var(--border-strong))] hover:border-[hsl(var(--brand-primary))]',\n 'peer-focus-visible:ring-2 peer-focus-visible:ring-[hsl(var(--brand-primary))] peer-focus-visible:ring-offset-2 peer-focus-visible:ring-offset-[hsl(var(--bg-base))]',\n 'peer-disabled:opacity-50 peer-disabled:pointer-events-none',\n )}\n >\n {indeterminate ? (\n <Minus className={cn(iconSize, 'text-[hsl(var(--text-on-brand))] stroke-[3]')} />\n ) : checked ? (\n <Check className={cn(iconSize, 'text-[hsl(var(--text-on-brand))] stroke-[3]')} />\n ) : null}\n </div>\n </label>\n )\n },\n)\n\nCheckbox.displayName = 'Checkbox'\n\nexport { Checkbox }\n","'use client'\n\nimport type React from 'react'\nimport { ToggleLeft, ToggleRight } from 'lucide-react'\nimport { cn } from '../utils'\n\nexport interface ToggleSwitchProps {\n /** Whether the toggle is on. */\n enabled: boolean\n /** Callback when toggled. */\n onChange: (enabled: boolean) => void\n /** Size variant. */\n size?: 'sm' | 'md'\n /** Disable the toggle. */\n disabled?: boolean\n /** Accessible label. */\n label?: string\n className?: string\n}\n\n/**\n * @description A themed toggle switch using lucide icons for on/off states.\n * Supports dark/light mode via CSS custom property tokens.\n */\nexport function ToggleSwitch({\n enabled, onChange, size = 'md', disabled, label, className,\n}: ToggleSwitchProps): React.JSX.Element {\n const iconSize = size === 'sm' ? 'size-5' : 'size-6'\n\n return (\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={enabled}\n aria-label={label}\n disabled={disabled}\n onClick={() => onChange(!enabled)}\n className={cn(\n 'inline-flex items-center transition-colors',\n 'text-[hsl(var(--text-secondary))] hover:text-[hsl(var(--text-primary))]',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n className,\n )}\n >\n {enabled ? (\n <ToggleRight className={cn(iconSize, 'text-[hsl(var(--status-ok))]')} />\n ) : (\n <ToggleLeft className={cn(iconSize, 'text-[hsl(var(--text-tertiary))]')} />\n )}\n </button>\n )\n}\n"]}
|