@ash-cloud/ash-ui 0.0.1 → 0.0.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/index.cjs CHANGED
@@ -1930,6 +1930,203 @@ function TodoPanel({
1930
1930
  }
1931
1931
  );
1932
1932
  }
1933
+ function EnvVarsPanel({
1934
+ envVars,
1935
+ onChange,
1936
+ defaultCollapsed = true,
1937
+ className,
1938
+ label = "Environment Variables",
1939
+ helperText = "These environment variables will be available in the sandbox for new sessions."
1940
+ }) {
1941
+ const [expanded, setExpanded] = react.useState(!defaultCollapsed);
1942
+ const [newEnvKey, setNewEnvKey] = react.useState("");
1943
+ const [newEnvValue, setNewEnvValue] = react.useState("");
1944
+ const hasEnvVars = Object.keys(envVars).length > 0;
1945
+ const handleAddEnvVar = react.useCallback(() => {
1946
+ const key = newEnvKey.trim();
1947
+ const val = newEnvValue.trim();
1948
+ if (key) {
1949
+ onChange({ ...envVars, [key]: val });
1950
+ setNewEnvKey("");
1951
+ setNewEnvValue("");
1952
+ }
1953
+ }, [envVars, newEnvKey, newEnvValue, onChange]);
1954
+ const handleRemoveEnvVar = react.useCallback(
1955
+ (key) => {
1956
+ const newEnvVars = { ...envVars };
1957
+ delete newEnvVars[key];
1958
+ onChange(newEnvVars);
1959
+ },
1960
+ [envVars, onChange]
1961
+ );
1962
+ const handleEnvKeyDown = react.useCallback(
1963
+ (e) => {
1964
+ if (e.key === "Enter") {
1965
+ e.preventDefault();
1966
+ handleAddEnvVar();
1967
+ }
1968
+ },
1969
+ [handleAddEnvVar]
1970
+ );
1971
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("ash-env-vars-panel", className), children: [
1972
+ /* @__PURE__ */ jsxRuntime.jsxs(
1973
+ "button",
1974
+ {
1975
+ type: "button",
1976
+ onClick: () => setExpanded(!expanded),
1977
+ className: "ash-env-vars-header",
1978
+ children: [
1979
+ /* @__PURE__ */ jsxRuntime.jsx(
1980
+ "svg",
1981
+ {
1982
+ xmlns: "http://www.w3.org/2000/svg",
1983
+ className: cn("ash-env-vars-chevron", expanded && "ash-env-vars-chevron-expanded"),
1984
+ viewBox: "0 0 20 20",
1985
+ fill: "currentColor",
1986
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1987
+ "path",
1988
+ {
1989
+ fillRule: "evenodd",
1990
+ d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z",
1991
+ clipRule: "evenodd"
1992
+ }
1993
+ )
1994
+ }
1995
+ ),
1996
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-label", children: label }),
1997
+ hasEnvVars && !expanded && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-badge", children: Object.keys(envVars).length })
1998
+ ]
1999
+ }
2000
+ ),
2001
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-env-vars-content", children: [
2002
+ Object.entries(envVars).map(([key, val]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-env-vars-item", children: [
2003
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-key", children: key }),
2004
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-equals", children: "=" }),
2005
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-value", children: val || "(empty)" }),
2006
+ /* @__PURE__ */ jsxRuntime.jsx(
2007
+ "button",
2008
+ {
2009
+ type: "button",
2010
+ onClick: () => handleRemoveEnvVar(key),
2011
+ className: "ash-env-vars-remove",
2012
+ title: "Remove variable",
2013
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2014
+ "svg",
2015
+ {
2016
+ xmlns: "http://www.w3.org/2000/svg",
2017
+ className: "w-4 h-4",
2018
+ viewBox: "0 0 20 20",
2019
+ fill: "currentColor",
2020
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2021
+ "path",
2022
+ {
2023
+ fillRule: "evenodd",
2024
+ d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",
2025
+ clipRule: "evenodd"
2026
+ }
2027
+ )
2028
+ }
2029
+ )
2030
+ }
2031
+ )
2032
+ ] }, key)),
2033
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ash-env-vars-add", children: [
2034
+ /* @__PURE__ */ jsxRuntime.jsx(
2035
+ "input",
2036
+ {
2037
+ type: "text",
2038
+ value: newEnvKey,
2039
+ onChange: (e) => setNewEnvKey(e.target.value.toUpperCase().replace(/[^A-Z0-9_]/g, "")),
2040
+ onKeyDown: handleEnvKeyDown,
2041
+ placeholder: "KEY",
2042
+ className: "ash-env-vars-input ash-env-vars-input-key"
2043
+ }
2044
+ ),
2045
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-env-vars-equals", children: "=" }),
2046
+ /* @__PURE__ */ jsxRuntime.jsx(
2047
+ "input",
2048
+ {
2049
+ type: "text",
2050
+ value: newEnvValue,
2051
+ onChange: (e) => setNewEnvValue(e.target.value),
2052
+ onKeyDown: handleEnvKeyDown,
2053
+ placeholder: "value",
2054
+ className: "ash-env-vars-input ash-env-vars-input-value"
2055
+ }
2056
+ ),
2057
+ /* @__PURE__ */ jsxRuntime.jsx(
2058
+ "button",
2059
+ {
2060
+ type: "button",
2061
+ onClick: handleAddEnvVar,
2062
+ disabled: !newEnvKey.trim(),
2063
+ className: "ash-env-vars-add-button",
2064
+ children: "Add"
2065
+ }
2066
+ )
2067
+ ] }),
2068
+ helperText && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "ash-env-vars-helper", children: helperText })
2069
+ ] })
2070
+ ] });
2071
+ }
2072
+ function DisplayModeToggle({
2073
+ className,
2074
+ showLabel = true,
2075
+ labels = { inline: "Inline", compact: "Compact" }
2076
+ }) {
2077
+ const { config, toggleMode } = useDisplayMode();
2078
+ const isInline = config.mode === "inline";
2079
+ return /* @__PURE__ */ jsxRuntime.jsx(
2080
+ "button",
2081
+ {
2082
+ type: "button",
2083
+ onClick: toggleMode,
2084
+ className: cn("ash-display-mode-toggle", className),
2085
+ title: isInline ? "Switch to compact mode" : "Switch to inline mode",
2086
+ children: isInline ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2087
+ /* @__PURE__ */ jsxRuntime.jsx(
2088
+ "svg",
2089
+ {
2090
+ className: "ash-display-mode-icon",
2091
+ viewBox: "0 0 24 24",
2092
+ fill: "none",
2093
+ stroke: "currentColor",
2094
+ strokeWidth: "1.5",
2095
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2096
+ "path",
2097
+ {
2098
+ strokeLinecap: "round",
2099
+ strokeLinejoin: "round",
2100
+ d: "M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5"
2101
+ }
2102
+ )
2103
+ }
2104
+ ),
2105
+ showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: labels.inline })
2106
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2107
+ /* @__PURE__ */ jsxRuntime.jsx(
2108
+ "svg",
2109
+ {
2110
+ className: "ash-display-mode-icon",
2111
+ viewBox: "0 0 24 24",
2112
+ fill: "none",
2113
+ stroke: "currentColor",
2114
+ strokeWidth: "1.5",
2115
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2116
+ "path",
2117
+ {
2118
+ strokeLinecap: "round",
2119
+ strokeLinejoin: "round",
2120
+ d: "M3.75 6.75h16.5M3.75 12h16.5M12 17.25h8.25"
2121
+ }
2122
+ )
2123
+ }
2124
+ ),
2125
+ showLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ash-display-mode-label", children: labels.compact })
2126
+ ] })
2127
+ }
2128
+ );
2129
+ }
1933
2130
  var DEFAULT_WORDS = [
1934
2131
  "Thinking",
1935
2132
  "Reasoning",
@@ -2318,6 +2515,225 @@ var inlineStyles = {
2318
2515
  borderRadius: borderRadius.lg
2319
2516
  }
2320
2517
  };
2518
+ function useMessageQueue({
2519
+ onProcessMessage,
2520
+ canProcess = true
2521
+ }) {
2522
+ const [queue, setQueue] = react.useState([]);
2523
+ const [isProcessing, setIsProcessing] = react.useState(false);
2524
+ const isProcessingRef = react.useRef(false);
2525
+ const onProcessMessageRef = react.useRef(onProcessMessage);
2526
+ react.useEffect(() => {
2527
+ onProcessMessageRef.current = onProcessMessage;
2528
+ }, [onProcessMessage]);
2529
+ const processQueue = react.useCallback(async () => {
2530
+ if (isProcessingRef.current || !canProcess) return;
2531
+ isProcessingRef.current = true;
2532
+ setIsProcessing(true);
2533
+ while (true) {
2534
+ let nextMessage;
2535
+ setQueue((prev) => {
2536
+ if (prev.length === 0) return prev;
2537
+ nextMessage = prev[0];
2538
+ return prev.slice(1);
2539
+ });
2540
+ await new Promise((resolve) => setTimeout(resolve, 0));
2541
+ if (!nextMessage) break;
2542
+ try {
2543
+ await onProcessMessageRef.current(nextMessage);
2544
+ } catch (error) {
2545
+ console.error("Error processing queued message:", error);
2546
+ }
2547
+ }
2548
+ isProcessingRef.current = false;
2549
+ setIsProcessing(false);
2550
+ }, [canProcess]);
2551
+ react.useEffect(() => {
2552
+ if (queue.length > 0 && !isProcessingRef.current && canProcess) {
2553
+ processQueue();
2554
+ }
2555
+ }, [queue, processQueue, canProcess]);
2556
+ const enqueue = react.useCallback((text, files) => {
2557
+ const message = {
2558
+ id: `queue-${Date.now()}-${Math.random().toString(36).slice(2)}`,
2559
+ text,
2560
+ files,
2561
+ queuedAt: Date.now()
2562
+ };
2563
+ setQueue((prev) => [...prev, message]);
2564
+ }, []);
2565
+ const cancel = react.useCallback((id) => {
2566
+ setQueue((prev) => prev.filter((m) => m.id !== id));
2567
+ }, []);
2568
+ const clearQueue = react.useCallback(() => {
2569
+ setQueue([]);
2570
+ }, []);
2571
+ return {
2572
+ queue,
2573
+ isProcessing,
2574
+ enqueue,
2575
+ cancel,
2576
+ clearQueue,
2577
+ queueLength: queue.length
2578
+ };
2579
+ }
2580
+ function useStopExecution({
2581
+ onServerStop
2582
+ } = {}) {
2583
+ const [canStop, setCanStop] = react.useState(false);
2584
+ const abortControllerRef = react.useRef(null);
2585
+ const sessionIdRef = react.useRef(null);
2586
+ const onServerStopRef = react.useRef(onServerStop);
2587
+ onServerStopRef.current = onServerStop;
2588
+ const startExecution = react.useCallback(() => {
2589
+ abortControllerRef.current = new AbortController();
2590
+ setCanStop(true);
2591
+ }, []);
2592
+ const endExecution = react.useCallback(() => {
2593
+ abortControllerRef.current = null;
2594
+ sessionIdRef.current = null;
2595
+ setCanStop(false);
2596
+ }, []);
2597
+ const setSessionId = react.useCallback((sessionId) => {
2598
+ sessionIdRef.current = sessionId;
2599
+ }, []);
2600
+ const stop = react.useCallback(async (explicitSessionId) => {
2601
+ if (abortControllerRef.current) {
2602
+ abortControllerRef.current.abort();
2603
+ }
2604
+ const sessionId = explicitSessionId || sessionIdRef.current;
2605
+ if (sessionId && onServerStopRef.current) {
2606
+ try {
2607
+ await onServerStopRef.current(sessionId);
2608
+ } catch (err) {
2609
+ console.warn("Server stop failed:", err);
2610
+ }
2611
+ }
2612
+ setCanStop(false);
2613
+ }, []);
2614
+ return {
2615
+ canStop,
2616
+ signal: abortControllerRef.current?.signal,
2617
+ startExecution,
2618
+ endExecution,
2619
+ stop,
2620
+ setSessionId
2621
+ };
2622
+ }
2623
+ function formatFileSize2(bytes) {
2624
+ if (bytes < 1024) return `${bytes} B`;
2625
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
2626
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2627
+ }
2628
+ async function readFileAsBase64(file) {
2629
+ return new Promise((resolve, reject) => {
2630
+ const reader = new FileReader();
2631
+ reader.onload = () => {
2632
+ const result = reader.result;
2633
+ const base64 = result.split(",")[1];
2634
+ resolve(base64 || "");
2635
+ };
2636
+ reader.onerror = () => reject(reader.error);
2637
+ reader.readAsDataURL(file);
2638
+ });
2639
+ }
2640
+ function useFileUpload({
2641
+ maxFileSize = 100 * 1024 * 1024,
2642
+ // 100MB
2643
+ maxFiles = 10,
2644
+ onValidationError
2645
+ } = {}) {
2646
+ const [files, setFiles] = react.useState([]);
2647
+ const [isDragOver, setIsDragOver] = react.useState(false);
2648
+ const fileInputRef = react.useRef(null);
2649
+ const addFiles = react.useCallback(
2650
+ async (fileList) => {
2651
+ if (!fileList) return;
2652
+ const newFiles = [];
2653
+ const currentCount = files.length;
2654
+ for (let i = 0; i < fileList.length && currentCount + newFiles.length < maxFiles; i++) {
2655
+ const file = fileList.item(i);
2656
+ if (!file) continue;
2657
+ if (file.size > maxFileSize) {
2658
+ onValidationError?.(
2659
+ file.name,
2660
+ `Exceeds maximum size of ${formatFileSize2(maxFileSize)}`
2661
+ );
2662
+ continue;
2663
+ }
2664
+ try {
2665
+ const content = await readFileAsBase64(file);
2666
+ newFiles.push({
2667
+ filename: file.name,
2668
+ content,
2669
+ mimeType: file.type || "application/octet-stream",
2670
+ size: file.size
2671
+ });
2672
+ } catch (error) {
2673
+ console.error(`Failed to read file ${file.name}:`, error);
2674
+ onValidationError?.(file.name, "Failed to read file");
2675
+ }
2676
+ }
2677
+ if (fileList.length > maxFiles - currentCount && currentCount + newFiles.length >= maxFiles) {
2678
+ onValidationError?.("", `Maximum ${maxFiles} files allowed`);
2679
+ }
2680
+ if (newFiles.length > 0) {
2681
+ setFiles((prev) => [...prev, ...newFiles]);
2682
+ }
2683
+ },
2684
+ [files.length, maxFiles, maxFileSize, onValidationError]
2685
+ );
2686
+ const removeFile = react.useCallback((index) => {
2687
+ setFiles((prev) => prev.filter((_, i) => i !== index));
2688
+ }, []);
2689
+ const clearFiles = react.useCallback(() => {
2690
+ setFiles([]);
2691
+ }, []);
2692
+ const handleDragOver = react.useCallback((e) => {
2693
+ e.preventDefault();
2694
+ setIsDragOver(true);
2695
+ }, []);
2696
+ const handleDragLeave = react.useCallback((e) => {
2697
+ e.preventDefault();
2698
+ setIsDragOver(false);
2699
+ }, []);
2700
+ const handleDrop = react.useCallback(
2701
+ (e) => {
2702
+ e.preventDefault();
2703
+ setIsDragOver(false);
2704
+ addFiles(e.dataTransfer.files);
2705
+ },
2706
+ [addFiles]
2707
+ );
2708
+ const handleFileInputChange = react.useCallback(
2709
+ (e) => {
2710
+ addFiles(e.target.files);
2711
+ if (fileInputRef.current) {
2712
+ fileInputRef.current.value = "";
2713
+ }
2714
+ },
2715
+ [addFiles]
2716
+ );
2717
+ const openFilePicker = react.useCallback(() => {
2718
+ fileInputRef.current?.click();
2719
+ }, []);
2720
+ return {
2721
+ files,
2722
+ isDragOver,
2723
+ addFiles,
2724
+ removeFile,
2725
+ clearFiles,
2726
+ canAddMore: files.length < maxFiles,
2727
+ dropZoneProps: {
2728
+ onDragOver: handleDragOver,
2729
+ onDragLeave: handleDragLeave,
2730
+ onDrop: handleDrop
2731
+ },
2732
+ handleFileInputChange,
2733
+ fileInputRef,
2734
+ openFilePicker
2735
+ };
2736
+ }
2321
2737
 
2322
2738
  exports.ActionIcon = ActionIcon;
2323
2739
  exports.AlertCircleIcon = AlertCircleIcon;
@@ -2340,7 +2756,9 @@ exports.CompactToolStatusLine = CompactToolStatusLine;
2340
2756
  exports.CopyIcon = CopyIcon;
2341
2757
  exports.DEFAULT_DISPLAY_CONFIG = DEFAULT_DISPLAY_CONFIG;
2342
2758
  exports.DisplayModeProvider = DisplayModeProvider;
2759
+ exports.DisplayModeToggle = DisplayModeToggle;
2343
2760
  exports.EditIcon = EditIcon;
2761
+ exports.EnvVarsPanel = EnvVarsPanel;
2344
2762
  exports.ErrorMessage = ErrorMessage;
2345
2763
  exports.FileIcon = FileIcon;
2346
2764
  exports.FilePlusIcon = FilePlusIcon;
@@ -2423,6 +2841,9 @@ exports.typography = typography;
2423
2841
  exports.updateToolCallWithResult = updateToolCallWithResult;
2424
2842
  exports.useDisplayConfig = useDisplayConfig;
2425
2843
  exports.useDisplayMode = useDisplayMode;
2844
+ exports.useFileUpload = useFileUpload;
2845
+ exports.useMessageQueue = useMessageQueue;
2846
+ exports.useStopExecution = useStopExecution;
2426
2847
  exports.useTheme = useTheme;
2427
2848
  exports.widget = widget;
2428
2849
  exports.zIndex = zIndex;