@windrun-huaiin/third-ui 29.0.0 → 29.0.1
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/fuma/server/site-mdx-base.js +11 -1
- package/dist/fuma/server/site-mdx-base.mjs +11 -1
- package/dist/fuma/server/site-mdx-fallbacks.js +1 -1
- package/dist/fuma/server/site-mdx-fallbacks.mjs +1 -1
- package/dist/main/money-price/money-price-button.js +1 -1
- package/dist/main/money-price/money-price-button.mjs +1 -1
- package/package.json +1 -1
- package/src/fuma/server/site-mdx-base.tsx +19 -2
- package/src/fuma/server/site-mdx-fallbacks.tsx +1 -1
- package/src/main/money-price/money-price-button.tsx +1 -1
- package/dist/fuma/base/site-layout.js +0 -72
- package/dist/fuma/base/site-layout.mjs +0 -65
- package/dist/fuma/mdx/gradient-button.d.ts +0 -20
- package/dist/fuma/mdx/gradient-button.js +0 -96
- package/dist/fuma/mdx/gradient-button.mjs +0 -94
- package/dist/lib/site-docs-helper.d.ts +0 -51
- package/dist/lib/site-docs-helper.js +0 -68
- package/dist/lib/site-docs-helper.mjs +0 -66
- package/dist/main/ai-prompt-textarea.d.ts +0 -72
- package/dist/main/ai-prompt-textarea.js +0 -114
- package/dist/main/ai-prompt-textarea.mjs +0 -112
- package/dist/main/x-button.d.ts +0 -39
- package/dist/main/x-button.js +0 -102
- package/dist/main/x-button.mjs +0 -100
- package/dist/main/x-toggle-button.d.ts +0 -32
- package/dist/main/x-toggle-button.js +0 -95
- package/dist/main/x-toggle-button.mjs +0 -74
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var tslib = require('tslib');
|
|
4
|
-
var server = require('@windrun-huaiin/base-ui/components/server');
|
|
5
|
-
var source = require('@windrun-huaiin/fumadocs-local-md/server/source');
|
|
6
|
-
var base = require('@windrun-huaiin/fumadocs-local-md/presets/fuma-docs/base');
|
|
7
|
-
var fumaSchemaCheckUtil = require('./fuma-schema-check-util.js');
|
|
8
|
-
var siteMdxBase = require('../fuma/server/site-mdx-base.js');
|
|
9
|
-
|
|
10
|
-
function resolveLocalMdMode() {
|
|
11
|
-
var _a;
|
|
12
|
-
const enableDevRuntime = ((_a = process.env.LOCAL_MD_DEV_RUNTIME) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
|
|
13
|
-
if (process.env.NODE_ENV !== 'production' && enableDevRuntime) {
|
|
14
|
-
return 'runtime';
|
|
15
|
-
}
|
|
16
|
-
return 'build';
|
|
17
|
-
}
|
|
18
|
-
function createSiteDocsI18nConfig(appConfig) {
|
|
19
|
-
return {
|
|
20
|
-
defaultLanguage: appConfig.i18n.defaultLocale,
|
|
21
|
-
languages: appConfig.i18n.locales,
|
|
22
|
-
hideLocale: appConfig.i18n.localePrefixAsNeeded ? 'default-locale' : 'never',
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
function createSiteDocsHelper(options) {
|
|
26
|
-
const { appConfig, sourceRootDir, appRoot = process.cwd(), sourceFeatures = [], mdxFeatures = [], additionalComponents, frontmatterSchema = fumaSchemaCheckUtil.createCommonDocsSchema(), metaSchema = fumaSchemaCheckUtil.createCommonMetaSchema(), } = options;
|
|
27
|
-
const i18n = createSiteDocsI18nConfig(appConfig);
|
|
28
|
-
const sharedConfig = {
|
|
29
|
-
i18n,
|
|
30
|
-
frontmatterSchema,
|
|
31
|
-
metaSchema,
|
|
32
|
-
sourceRootDir,
|
|
33
|
-
appRoot,
|
|
34
|
-
icon(icon) {
|
|
35
|
-
return server.getGlobalIcon(icon, true);
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
const buildConfig = Object.assign(Object.assign({}, sharedConfig), base.createFumaDocsBaseCompilerOptions({
|
|
39
|
-
features: sourceFeatures,
|
|
40
|
-
}));
|
|
41
|
-
const sourceFactory = source.createConfiguredLocalMdSourceFactory(sharedConfig);
|
|
42
|
-
const getMDXComponents = siteMdxBase.createSiteMdxComponents({
|
|
43
|
-
baseOptions: {
|
|
44
|
-
imageFallbackSrc: appConfig.style.placeHolder.image,
|
|
45
|
-
cdnBaseUrl: appConfig.style.cdnBaseUrl,
|
|
46
|
-
},
|
|
47
|
-
features: mdxFeatures,
|
|
48
|
-
additionalComponents: Object.assign(Object.assign({}, server.globalLucideIcons), additionalComponents),
|
|
49
|
-
});
|
|
50
|
-
function getContentSource(sourceKey, overrides) {
|
|
51
|
-
return tslib.__awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
return sourceFactory.getCachedSource(sourceKey, Object.assign({ mode: resolveLocalMdMode() }, overrides));
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
i18n,
|
|
57
|
-
buildConfig,
|
|
58
|
-
sourceFactory,
|
|
59
|
-
getContentSource,
|
|
60
|
-
getMDXComponents,
|
|
61
|
-
useMDXComponents: getMDXComponents,
|
|
62
|
-
createSourceSharedConfig() {
|
|
63
|
-
return Object.assign({}, sharedConfig);
|
|
64
|
-
},
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
exports.createSiteDocsHelper = createSiteDocsHelper;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { __awaiter } from 'tslib';
|
|
2
|
-
import { globalLucideIcons, getGlobalIcon } from '@windrun-huaiin/base-ui/components/server';
|
|
3
|
-
import { createConfiguredLocalMdSourceFactory } from '@windrun-huaiin/fumadocs-local-md/server/source';
|
|
4
|
-
import { createFumaDocsBaseCompilerOptions } from '@windrun-huaiin/fumadocs-local-md/presets/fuma-docs/base';
|
|
5
|
-
import { createCommonMetaSchema, createCommonDocsSchema } from './fuma-schema-check-util.mjs';
|
|
6
|
-
import { createSiteMdxComponents } from '../fuma/server/site-mdx-base.mjs';
|
|
7
|
-
|
|
8
|
-
function resolveLocalMdMode() {
|
|
9
|
-
var _a;
|
|
10
|
-
const enableDevRuntime = ((_a = process.env.LOCAL_MD_DEV_RUNTIME) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
|
|
11
|
-
if (process.env.NODE_ENV !== 'production' && enableDevRuntime) {
|
|
12
|
-
return 'runtime';
|
|
13
|
-
}
|
|
14
|
-
return 'build';
|
|
15
|
-
}
|
|
16
|
-
function createSiteDocsI18nConfig(appConfig) {
|
|
17
|
-
return {
|
|
18
|
-
defaultLanguage: appConfig.i18n.defaultLocale,
|
|
19
|
-
languages: appConfig.i18n.locales,
|
|
20
|
-
hideLocale: appConfig.i18n.localePrefixAsNeeded ? 'default-locale' : 'never',
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function createSiteDocsHelper(options) {
|
|
24
|
-
const { appConfig, sourceRootDir, appRoot = process.cwd(), sourceFeatures = [], mdxFeatures = [], additionalComponents, frontmatterSchema = createCommonDocsSchema(), metaSchema = createCommonMetaSchema(), } = options;
|
|
25
|
-
const i18n = createSiteDocsI18nConfig(appConfig);
|
|
26
|
-
const sharedConfig = {
|
|
27
|
-
i18n,
|
|
28
|
-
frontmatterSchema,
|
|
29
|
-
metaSchema,
|
|
30
|
-
sourceRootDir,
|
|
31
|
-
appRoot,
|
|
32
|
-
icon(icon) {
|
|
33
|
-
return getGlobalIcon(icon, true);
|
|
34
|
-
},
|
|
35
|
-
};
|
|
36
|
-
const buildConfig = Object.assign(Object.assign({}, sharedConfig), createFumaDocsBaseCompilerOptions({
|
|
37
|
-
features: sourceFeatures,
|
|
38
|
-
}));
|
|
39
|
-
const sourceFactory = createConfiguredLocalMdSourceFactory(sharedConfig);
|
|
40
|
-
const getMDXComponents = createSiteMdxComponents({
|
|
41
|
-
baseOptions: {
|
|
42
|
-
imageFallbackSrc: appConfig.style.placeHolder.image,
|
|
43
|
-
cdnBaseUrl: appConfig.style.cdnBaseUrl,
|
|
44
|
-
},
|
|
45
|
-
features: mdxFeatures,
|
|
46
|
-
additionalComponents: Object.assign(Object.assign({}, globalLucideIcons), additionalComponents),
|
|
47
|
-
});
|
|
48
|
-
function getContentSource(sourceKey, overrides) {
|
|
49
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
return sourceFactory.getCachedSource(sourceKey, Object.assign({ mode: resolveLocalMdMode() }, overrides));
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
i18n,
|
|
55
|
-
buildConfig,
|
|
56
|
-
sourceFactory,
|
|
57
|
-
getContentSource,
|
|
58
|
-
getMDXComponents,
|
|
59
|
-
useMDXComponents: getMDXComponents,
|
|
60
|
-
createSourceSharedConfig() {
|
|
61
|
-
return Object.assign({}, sharedConfig);
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export { createSiteDocsHelper };
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
interface AIPromptTextareaProps {
|
|
2
|
-
/**
|
|
3
|
-
* Textarea value reference
|
|
4
|
-
*/
|
|
5
|
-
value: string;
|
|
6
|
-
/**
|
|
7
|
-
* Textarea value change handler
|
|
8
|
-
*/
|
|
9
|
-
onChange: (value: string) => void;
|
|
10
|
-
/**
|
|
11
|
-
* Word limit value reference
|
|
12
|
-
*/
|
|
13
|
-
isWordLimit: boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Word limit value change handler
|
|
16
|
-
*/
|
|
17
|
-
onWordLimitChange: (isLimit: boolean) => void;
|
|
18
|
-
/**
|
|
19
|
-
* Placeholder
|
|
20
|
-
*/
|
|
21
|
-
placeholder?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Disabled switch condition, default is false
|
|
24
|
-
*/
|
|
25
|
-
disabled?: boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Maximum words
|
|
28
|
-
*/
|
|
29
|
-
maxWords?: number;
|
|
30
|
-
/**
|
|
31
|
-
* Word count unit title
|
|
32
|
-
*/
|
|
33
|
-
wordUnitTitle?: string;
|
|
34
|
-
/**
|
|
35
|
-
* Minimum height, px
|
|
36
|
-
*/
|
|
37
|
-
minHeight?: number;
|
|
38
|
-
/**
|
|
39
|
-
* Maximum height, px
|
|
40
|
-
*/
|
|
41
|
-
maxHeight?: number;
|
|
42
|
-
/**
|
|
43
|
-
* Word count switch, default is true
|
|
44
|
-
*/
|
|
45
|
-
showWordCount?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Auto scroll switch, default is true
|
|
48
|
-
*/
|
|
49
|
-
autoScroll?: boolean;
|
|
50
|
-
/**
|
|
51
|
-
* Extra scroll space, px
|
|
52
|
-
*/
|
|
53
|
-
extraScrollSpace?: number;
|
|
54
|
-
/**
|
|
55
|
-
* Custome tailwindcss style
|
|
56
|
-
*/
|
|
57
|
-
className?: string;
|
|
58
|
-
/**
|
|
59
|
-
* Title text, if not provided, no title will be rendered
|
|
60
|
-
*/
|
|
61
|
-
title?: string;
|
|
62
|
-
/**
|
|
63
|
-
* Description text
|
|
64
|
-
*/
|
|
65
|
-
description?: string;
|
|
66
|
-
/**
|
|
67
|
-
* Embed title inside textarea, default is false
|
|
68
|
-
*/
|
|
69
|
-
embed?: boolean;
|
|
70
|
-
}
|
|
71
|
-
export declare function AIPromptTextarea({ value, onChange, placeholder, disabled, maxWords, wordUnitTitle, minHeight, maxHeight, className, showWordCount, autoScroll, extraScrollSpace, isWordLimit, onWordLimitChange, title, description, embed }: AIPromptTextareaProps): import("react/jsx-runtime").JSX.Element;
|
|
72
|
-
export {};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
-
var React = require('react');
|
|
6
|
-
var utils = require('@windrun-huaiin/lib/utils');
|
|
7
|
-
|
|
8
|
-
function AIPromptTextarea({ value, onChange, placeholder = "Enter your prompt...", disabled = false, maxWords = 400, wordUnitTitle = "words", minHeight = 150, maxHeight = 300, className = "", showWordCount = true, autoScroll = true, extraScrollSpace = 100, isWordLimit, onWordLimitChange, title, description, embed = false }) {
|
|
9
|
-
const textareaRef = React.useRef(null);
|
|
10
|
-
// count words
|
|
11
|
-
const wordArray = value.trim().split(/\s+/).filter(Boolean);
|
|
12
|
-
const wordCount = wordArray.length;
|
|
13
|
-
// auto adjust textarea height
|
|
14
|
-
const adjustTextareaHeight = () => {
|
|
15
|
-
if (textareaRef.current) {
|
|
16
|
-
const textarea = textareaRef.current;
|
|
17
|
-
const oldHeight = textarea.style.height;
|
|
18
|
-
// reset height
|
|
19
|
-
textarea.style.height = 'auto';
|
|
20
|
-
// calculate content height
|
|
21
|
-
const contentHeight = textarea.scrollHeight;
|
|
22
|
-
// auto adjust height between min and max height
|
|
23
|
-
let newHeight = Math.max(contentHeight, minHeight);
|
|
24
|
-
newHeight = Math.min(newHeight, maxHeight);
|
|
25
|
-
textarea.style.height = `${newHeight}px`;
|
|
26
|
-
// if content height is greater than max height, show scrollbar
|
|
27
|
-
if (contentHeight > maxHeight) {
|
|
28
|
-
textarea.style.overflowY = 'auto';
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
textarea.style.overflowY = 'hidden';
|
|
32
|
-
}
|
|
33
|
-
// if height increased and auto scroll is enabled, scroll to appropriate position
|
|
34
|
-
if (autoScroll && (newHeight > parseInt(oldHeight) || !oldHeight)) {
|
|
35
|
-
setTimeout(() => {
|
|
36
|
-
const rect = textarea.getBoundingClientRect();
|
|
37
|
-
window.scrollTo({
|
|
38
|
-
top: window.pageYOffset + rect.bottom + extraScrollSpace - window.innerHeight,
|
|
39
|
-
behavior: 'smooth'
|
|
40
|
-
});
|
|
41
|
-
}, 0);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
// when value changes, adjust height
|
|
46
|
-
React.useEffect(() => {
|
|
47
|
-
const timer = setTimeout(() => {
|
|
48
|
-
adjustTextareaHeight();
|
|
49
|
-
}, 0);
|
|
50
|
-
return () => clearTimeout(timer);
|
|
51
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
52
|
-
}, [value, minHeight, maxHeight, autoScroll, extraScrollSpace]);
|
|
53
|
-
// handle input, limit max words
|
|
54
|
-
const handleInputChange = (e) => {
|
|
55
|
-
const inputValue = e.target.value;
|
|
56
|
-
const words = inputValue.trim().split(/\s+/).filter(Boolean);
|
|
57
|
-
// if already reached max words, and this input will exceed limit, do not update
|
|
58
|
-
if (wordCount >= maxWords && words.length > maxWords) {
|
|
59
|
-
onWordLimitChange(true);
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (words.length > maxWords) {
|
|
63
|
-
onChange(words.slice(0, maxWords).join(' '));
|
|
64
|
-
onWordLimitChange(true);
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
onChange(inputValue);
|
|
68
|
-
onWordLimitChange(false);
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
// when paste, also check word count
|
|
72
|
-
const handlePaste = (e) => {
|
|
73
|
-
const paste = e.clipboardData.getData('text');
|
|
74
|
-
const currentWords = value.trim().split(/\s+/).filter(Boolean);
|
|
75
|
-
const pasteWords = paste.trim().split(/\s+/).filter(Boolean);
|
|
76
|
-
if (currentWords.length >= maxWords) {
|
|
77
|
-
e.preventDefault();
|
|
78
|
-
onWordLimitChange(true);
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
// only allow paste remaining words
|
|
82
|
-
const allowed = maxWords - currentWords.length;
|
|
83
|
-
if (pasteWords.length > allowed) {
|
|
84
|
-
e.preventDefault();
|
|
85
|
-
const newWords = currentWords.concat(pasteWords.slice(0, allowed));
|
|
86
|
-
onChange(newWords.join(' '));
|
|
87
|
-
onWordLimitChange(true);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
// 渲染标题组件
|
|
91
|
-
const renderTitle = () => {
|
|
92
|
-
if (!(title === null || title === void 0 ? void 0 : title.trim()))
|
|
93
|
-
return null;
|
|
94
|
-
return (jsxRuntime.jsxs("div", { className: "space-y-1", children: [title && jsxRuntime.jsx("span", { className: "text-xl font-semibold text-foreground", children: title }), (description === null || description === void 0 ? void 0 : description.trim()) && jsxRuntime.jsx("span", { className: "text-sm text-gray-400 ml-2", children: description })] }));
|
|
95
|
-
};
|
|
96
|
-
// 渲染textarea组件
|
|
97
|
-
const renderTextarea = (isEmbedded = false) => (jsxRuntime.jsx("textarea", { ref: textareaRef, value: value, onChange: handleInputChange, onPaste: handlePaste, placeholder: placeholder, disabled: disabled, className: utils.cn('w-full p-4 bg-transparent transition-colors text-foreground placeholder-muted-foreground placeholder:text-base disabled:bg-muted disabled:cursor-not-allowed resize-none', isEmbedded
|
|
98
|
-
? 'border-0 hover:border-2 hover:border-purple-500 focus:outline-none focus:border-2 focus:border-purple-500'
|
|
99
|
-
: 'border-2 border-border rounded-lg hover:border-purple-500 focus:outline-none focus:border-purple-500', className), style: { minHeight: `${minHeight}px` } }));
|
|
100
|
-
// 渲染单词计数
|
|
101
|
-
const renderWordCount = () => {
|
|
102
|
-
if (!showWordCount)
|
|
103
|
-
return null;
|
|
104
|
-
return (jsxRuntime.jsx("div", { className: "flex justify-end", children: jsxRuntime.jsxs("span", { className: `text-sm ${wordCount >= maxWords ? 'text-red-500' : wordCount > maxWords * 0.75 ? 'text-orange-500' : 'text-muted-foreground'} ${isWordLimit ? 'animate-bounce' : ''}`, onAnimationEnd: () => onWordLimitChange(false), children: [wordCount, "/", maxWords, " ", wordUnitTitle] }) }));
|
|
105
|
-
};
|
|
106
|
-
// 如果有标题且需要嵌入,则渲染内部标题布局
|
|
107
|
-
if (embed && (title)) {
|
|
108
|
-
return (jsxRuntime.jsxs("div", { className: "space-y-2", children: [jsxRuntime.jsxs("div", { className: "border-2 border-border rounded-lg bg-transparent", children: [jsxRuntime.jsx("div", { className: "p-4 pb-2", children: renderTitle() }), jsxRuntime.jsx("hr", { className: "border-t border-border" }), jsxRuntime.jsx("div", { className: "p-1", children: renderTextarea(true) })] }), renderWordCount()] }));
|
|
109
|
-
}
|
|
110
|
-
// 默认布局:外部标题或无标题
|
|
111
|
-
return (jsxRuntime.jsxs("div", { className: "space-y-2", children: [renderTitle(), renderTextarea(), renderWordCount()] }));
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
exports.AIPromptTextarea = AIPromptTextarea;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
-
import { useRef, useEffect } from 'react';
|
|
4
|
-
import { cn } from '@windrun-huaiin/lib/utils';
|
|
5
|
-
|
|
6
|
-
function AIPromptTextarea({ value, onChange, placeholder = "Enter your prompt...", disabled = false, maxWords = 400, wordUnitTitle = "words", minHeight = 150, maxHeight = 300, className = "", showWordCount = true, autoScroll = true, extraScrollSpace = 100, isWordLimit, onWordLimitChange, title, description, embed = false }) {
|
|
7
|
-
const textareaRef = useRef(null);
|
|
8
|
-
// count words
|
|
9
|
-
const wordArray = value.trim().split(/\s+/).filter(Boolean);
|
|
10
|
-
const wordCount = wordArray.length;
|
|
11
|
-
// auto adjust textarea height
|
|
12
|
-
const adjustTextareaHeight = () => {
|
|
13
|
-
if (textareaRef.current) {
|
|
14
|
-
const textarea = textareaRef.current;
|
|
15
|
-
const oldHeight = textarea.style.height;
|
|
16
|
-
// reset height
|
|
17
|
-
textarea.style.height = 'auto';
|
|
18
|
-
// calculate content height
|
|
19
|
-
const contentHeight = textarea.scrollHeight;
|
|
20
|
-
// auto adjust height between min and max height
|
|
21
|
-
let newHeight = Math.max(contentHeight, minHeight);
|
|
22
|
-
newHeight = Math.min(newHeight, maxHeight);
|
|
23
|
-
textarea.style.height = `${newHeight}px`;
|
|
24
|
-
// if content height is greater than max height, show scrollbar
|
|
25
|
-
if (contentHeight > maxHeight) {
|
|
26
|
-
textarea.style.overflowY = 'auto';
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
textarea.style.overflowY = 'hidden';
|
|
30
|
-
}
|
|
31
|
-
// if height increased and auto scroll is enabled, scroll to appropriate position
|
|
32
|
-
if (autoScroll && (newHeight > parseInt(oldHeight) || !oldHeight)) {
|
|
33
|
-
setTimeout(() => {
|
|
34
|
-
const rect = textarea.getBoundingClientRect();
|
|
35
|
-
window.scrollTo({
|
|
36
|
-
top: window.pageYOffset + rect.bottom + extraScrollSpace - window.innerHeight,
|
|
37
|
-
behavior: 'smooth'
|
|
38
|
-
});
|
|
39
|
-
}, 0);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
// when value changes, adjust height
|
|
44
|
-
useEffect(() => {
|
|
45
|
-
const timer = setTimeout(() => {
|
|
46
|
-
adjustTextareaHeight();
|
|
47
|
-
}, 0);
|
|
48
|
-
return () => clearTimeout(timer);
|
|
49
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
50
|
-
}, [value, minHeight, maxHeight, autoScroll, extraScrollSpace]);
|
|
51
|
-
// handle input, limit max words
|
|
52
|
-
const handleInputChange = (e) => {
|
|
53
|
-
const inputValue = e.target.value;
|
|
54
|
-
const words = inputValue.trim().split(/\s+/).filter(Boolean);
|
|
55
|
-
// if already reached max words, and this input will exceed limit, do not update
|
|
56
|
-
if (wordCount >= maxWords && words.length > maxWords) {
|
|
57
|
-
onWordLimitChange(true);
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
if (words.length > maxWords) {
|
|
61
|
-
onChange(words.slice(0, maxWords).join(' '));
|
|
62
|
-
onWordLimitChange(true);
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
onChange(inputValue);
|
|
66
|
-
onWordLimitChange(false);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
// when paste, also check word count
|
|
70
|
-
const handlePaste = (e) => {
|
|
71
|
-
const paste = e.clipboardData.getData('text');
|
|
72
|
-
const currentWords = value.trim().split(/\s+/).filter(Boolean);
|
|
73
|
-
const pasteWords = paste.trim().split(/\s+/).filter(Boolean);
|
|
74
|
-
if (currentWords.length >= maxWords) {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
onWordLimitChange(true);
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// only allow paste remaining words
|
|
80
|
-
const allowed = maxWords - currentWords.length;
|
|
81
|
-
if (pasteWords.length > allowed) {
|
|
82
|
-
e.preventDefault();
|
|
83
|
-
const newWords = currentWords.concat(pasteWords.slice(0, allowed));
|
|
84
|
-
onChange(newWords.join(' '));
|
|
85
|
-
onWordLimitChange(true);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
// 渲染标题组件
|
|
89
|
-
const renderTitle = () => {
|
|
90
|
-
if (!(title === null || title === void 0 ? void 0 : title.trim()))
|
|
91
|
-
return null;
|
|
92
|
-
return (jsxs("div", { className: "space-y-1", children: [title && jsx("span", { className: "text-xl font-semibold text-foreground", children: title }), (description === null || description === void 0 ? void 0 : description.trim()) && jsx("span", { className: "text-sm text-gray-400 ml-2", children: description })] }));
|
|
93
|
-
};
|
|
94
|
-
// 渲染textarea组件
|
|
95
|
-
const renderTextarea = (isEmbedded = false) => (jsx("textarea", { ref: textareaRef, value: value, onChange: handleInputChange, onPaste: handlePaste, placeholder: placeholder, disabled: disabled, className: cn('w-full p-4 bg-transparent transition-colors text-foreground placeholder-muted-foreground placeholder:text-base disabled:bg-muted disabled:cursor-not-allowed resize-none', isEmbedded
|
|
96
|
-
? 'border-0 hover:border-2 hover:border-purple-500 focus:outline-none focus:border-2 focus:border-purple-500'
|
|
97
|
-
: 'border-2 border-border rounded-lg hover:border-purple-500 focus:outline-none focus:border-purple-500', className), style: { minHeight: `${minHeight}px` } }));
|
|
98
|
-
// 渲染单词计数
|
|
99
|
-
const renderWordCount = () => {
|
|
100
|
-
if (!showWordCount)
|
|
101
|
-
return null;
|
|
102
|
-
return (jsx("div", { className: "flex justify-end", children: jsxs("span", { className: `text-sm ${wordCount >= maxWords ? 'text-red-500' : wordCount > maxWords * 0.75 ? 'text-orange-500' : 'text-muted-foreground'} ${isWordLimit ? 'animate-bounce' : ''}`, onAnimationEnd: () => onWordLimitChange(false), children: [wordCount, "/", maxWords, " ", wordUnitTitle] }) }));
|
|
103
|
-
};
|
|
104
|
-
// 如果有标题且需要嵌入,则渲染内部标题布局
|
|
105
|
-
if (embed && (title)) {
|
|
106
|
-
return (jsxs("div", { className: "space-y-2", children: [jsxs("div", { className: "border-2 border-border rounded-lg bg-transparent", children: [jsx("div", { className: "p-4 pb-2", children: renderTitle() }), jsx("hr", { className: "border-t border-border" }), jsx("div", { className: "p-1", children: renderTextarea(true) })] }), renderWordCount()] }));
|
|
107
|
-
}
|
|
108
|
-
// 默认布局:外部标题或无标题
|
|
109
|
-
return (jsxs("div", { className: "space-y-2", children: [renderTitle(), renderTextarea(), renderWordCount()] }));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
export { AIPromptTextarea };
|
package/dist/main/x-button.d.ts
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
type XButtonVariant = 'default' | 'soft' | 'subtle';
|
|
3
|
-
interface BaseButtonConfig {
|
|
4
|
-
icon: ReactNode;
|
|
5
|
-
text: string;
|
|
6
|
-
onClick: () => void | Promise<void>;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
}
|
|
9
|
-
interface MenuItemConfig extends BaseButtonConfig {
|
|
10
|
-
tag?: {
|
|
11
|
-
text: string;
|
|
12
|
-
color?: string;
|
|
13
|
-
};
|
|
14
|
-
splitTopBorder?: boolean;
|
|
15
|
-
}
|
|
16
|
-
interface SingleButtonProps {
|
|
17
|
-
type: 'single';
|
|
18
|
-
button: BaseButtonConfig;
|
|
19
|
-
loadingText?: string;
|
|
20
|
-
minWidth?: string;
|
|
21
|
-
className?: string;
|
|
22
|
-
iconClassName?: string;
|
|
23
|
-
variant?: XButtonVariant;
|
|
24
|
-
}
|
|
25
|
-
interface SplitButtonProps {
|
|
26
|
-
type: 'split';
|
|
27
|
-
mainButton: BaseButtonConfig;
|
|
28
|
-
menuItems: MenuItemConfig[];
|
|
29
|
-
loadingText?: string;
|
|
30
|
-
menuWidth?: string;
|
|
31
|
-
className?: string;
|
|
32
|
-
mainButtonClassName?: string;
|
|
33
|
-
dropdownButtonClassName?: string;
|
|
34
|
-
iconClassName?: string;
|
|
35
|
-
variant?: XButtonVariant;
|
|
36
|
-
}
|
|
37
|
-
type xButtonProps = SingleButtonProps | SplitButtonProps;
|
|
38
|
-
export declare function XButton(props: xButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
-
export {};
|
package/dist/main/x-button.js
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
var tslib = require('tslib');
|
|
5
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
-
var React = require('react');
|
|
7
|
-
var icons = require('@windrun-huaiin/base-ui/icons');
|
|
8
|
-
var lib = require('@windrun-huaiin/base-ui/lib');
|
|
9
|
-
var utils = require('@windrun-huaiin/lib/utils');
|
|
10
|
-
|
|
11
|
-
function XButton(props) {
|
|
12
|
-
var _a, _b, _c;
|
|
13
|
-
const [isLoading, setIsLoading] = React.useState(false);
|
|
14
|
-
const [menuOpen, setMenuOpen] = React.useState(false);
|
|
15
|
-
const menuRef = React.useRef(null);
|
|
16
|
-
const { iconClassName } = props;
|
|
17
|
-
const defaultIconClass = "w-5 h-5";
|
|
18
|
-
const variant = (_a = props.variant) !== null && _a !== void 0 ? _a : 'default';
|
|
19
|
-
const finalIconClass = utils.cn(variant === 'default' ? '' : lib.themeIconColor, iconClassName || defaultIconClass);
|
|
20
|
-
const loadingIconClass = utils.cn(finalIconClass, "mr-1 animate-spin");
|
|
21
|
-
const chevronIconClass = "w-6 h-6";
|
|
22
|
-
const renderIcon = (icon) => {
|
|
23
|
-
if (React.isValidElement(icon)) {
|
|
24
|
-
return React.cloneElement(icon, {
|
|
25
|
-
className: utils.cn(finalIconClass, icon.props.className),
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return icon;
|
|
29
|
-
};
|
|
30
|
-
// click outside to close menu
|
|
31
|
-
React.useEffect(() => {
|
|
32
|
-
if (props.type === 'split') {
|
|
33
|
-
const handleClickOutside = (event) => {
|
|
34
|
-
if (menuRef.current && !menuRef.current.contains(event.target)) {
|
|
35
|
-
setMenuOpen(false);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
if (menuOpen) {
|
|
39
|
-
document.addEventListener('mousedown', handleClickOutside);
|
|
40
|
-
}
|
|
41
|
-
return () => {
|
|
42
|
-
document.removeEventListener('mousedown', handleClickOutside);
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
}, [menuOpen, props.type]);
|
|
46
|
-
// handle button click
|
|
47
|
-
const handleButtonClick = (onClick) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
48
|
-
if (isLoading)
|
|
49
|
-
return;
|
|
50
|
-
setIsLoading(true);
|
|
51
|
-
try {
|
|
52
|
-
yield onClick();
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
console.error('Button click error:', error);
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
setIsLoading(false);
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
// base style class
|
|
62
|
-
const baseButtonClass = "flex items-center justify-center gap-2 px-4 py-2 text-sm font-semibold transition-colors";
|
|
63
|
-
const singleButtonVariantClass = variant === 'soft'
|
|
64
|
-
? utils.cn(lib.themeBgColor, lib.themeIconColor, lib.themeBorderColor, "border hover:brightness-95")
|
|
65
|
-
: variant === 'subtle'
|
|
66
|
-
? utils.cn(lib.themeMainBgColor, lib.themeIconColor, "border border-neutral-200 hover:bg-neutral-50 dark:border-neutral-800 dark:hover:bg-neutral-800")
|
|
67
|
-
: "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700";
|
|
68
|
-
const splitMainButtonVariantClass = variant === 'soft'
|
|
69
|
-
? utils.cn("bg-transparent hover:bg-black/5 dark:hover:bg-white/5", lib.themeIconColor)
|
|
70
|
-
: variant === 'subtle'
|
|
71
|
-
? utils.cn("bg-transparent hover:bg-neutral-50 dark:hover:bg-neutral-800", lib.themeIconColor)
|
|
72
|
-
: "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700";
|
|
73
|
-
const splitDropdownVariantClass = variant === 'soft'
|
|
74
|
-
? utils.cn("bg-transparent hover:bg-black/5 dark:hover:bg-white/5 sm:border-l", lib.themeIconColor, lib.themeBorderColor)
|
|
75
|
-
: variant === 'subtle'
|
|
76
|
-
? utils.cn("bg-transparent hover:bg-neutral-50 dark:hover:bg-neutral-800 sm:border-l", lib.themeIconColor, "border-neutral-200 dark:border-neutral-800")
|
|
77
|
-
: "bg-neutral-200 dark:bg-neutral-800 text-neutral-700 dark:text-white hover:bg-neutral-300 dark:hover:bg-neutral-700 sm:border-l sm:border-neutral-300 sm:dark:border-neutral-700";
|
|
78
|
-
const disabledClass = "opacity-60 cursor-not-allowed";
|
|
79
|
-
if (props.type === 'single') {
|
|
80
|
-
const { button, loadingText, minWidth = 'min-w-[110px]', className = '' } = props;
|
|
81
|
-
const isDisabled = button.disabled || isLoading;
|
|
82
|
-
// loadingText: props.loadingText > button.text > 'Loading...'
|
|
83
|
-
const actualLoadingText = loadingText || ((_b = button.text) === null || _b === void 0 ? void 0 : _b.trim()) || 'Loading...';
|
|
84
|
-
return (jsxRuntime.jsx("button", { onClick: () => handleButtonClick(button.onClick), disabled: isDisabled, className: utils.cn("w-full sm:w-auto", minWidth, baseButtonClass, singleButtonVariantClass, "rounded-full", isDisabled && disabledClass, className), title: button.text, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(icons.Loader2Icon, { className: loadingIconClass }), jsxRuntime.jsx("span", { children: actualLoadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderIcon(button.icon), jsxRuntime.jsx("span", { children: button.text })] })) }));
|
|
85
|
-
}
|
|
86
|
-
// Split button
|
|
87
|
-
const { mainButton, menuItems, loadingText, menuWidth = 'w-full sm:w-40', className = '', mainButtonClassName = '', dropdownButtonClassName = '' } = props;
|
|
88
|
-
const isMainDisabled = mainButton.disabled || isLoading;
|
|
89
|
-
// loadingText prioty:props.loadingText > mainButton.text > 'Loading...'
|
|
90
|
-
const actualLoadingText = loadingText || ((_c = mainButton.text) === null || _c === void 0 ? void 0 : _c.trim()) || 'Loading...';
|
|
91
|
-
return (jsxRuntime.jsxs("div", { className: utils.cn("relative flex flex-row items-stretch w-full sm:w-auto rounded-full gap-0", menuOpen && "z-90", variant === 'soft'
|
|
92
|
-
? utils.cn(lib.themeBgColor, lib.themeBorderColor, "border")
|
|
93
|
-
: variant === 'subtle'
|
|
94
|
-
? utils.cn(lib.themeMainBgColor, "border border-neutral-200 dark:border-neutral-800")
|
|
95
|
-
: "bg-neutral-200 dark:bg-neutral-800", className), children: [jsxRuntime.jsx("button", { onClick: () => handleButtonClick(mainButton.onClick), disabled: isMainDisabled, className: utils.cn("min-w-0 flex-1", baseButtonClass, splitMainButtonVariantClass, "rounded-l-full rounded-r-none", isMainDisabled && disabledClass, mainButtonClassName), onMouseDown: e => { if (e.button === 2)
|
|
96
|
-
e.preventDefault(); }, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(icons.Loader2Icon, { className: loadingIconClass }), jsxRuntime.jsx("span", { children: actualLoadingText })] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [renderIcon(mainButton.icon), jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: mainButton.text })] })) }), jsxRuntime.jsx("button", { type: "button", className: utils.cn("flex h-full w-9 shrink-0 items-center justify-center px-0 py-1.5 cursor-pointer transition rounded-r-full rounded-l-none border-l sm:w-10", splitDropdownVariantClass, dropdownButtonClassName), onClick: e => { e.stopPropagation(); setMenuOpen(v => !v); }, "aria-label": "More actions", "aria-expanded": menuOpen, children: jsxRuntime.jsx(icons.ChevronDownIcon, { className: chevronIconClass }) }), menuOpen && (jsxRuntime.jsx("div", { ref: menuRef, className: `absolute right-0 top-full ${menuWidth} bg-white dark:bg-neutral-800 text-neutral-800 dark:text-white text-sm rounded-xl shadow-lg z-100 border border-neutral-200 dark:border-neutral-700 overflow-hidden animate-fade-in`, children: menuItems.map((item, index) => (jsxRuntime.jsxs("button", { onClick: () => {
|
|
97
|
-
handleButtonClick(item.onClick);
|
|
98
|
-
setMenuOpen(false);
|
|
99
|
-
}, disabled: item.disabled, className: `flex items-center w-full px-4 py-3 transition hover:bg-neutral-300 dark:hover:bg-neutral-600 text-left relative ${item.disabled ? disabledClass : ''}`, style: item.splitTopBorder ? { borderTop: '1px solid #AC62FD' } : undefined, children: [jsxRuntime.jsxs("span", { className: "flex items-center", children: [item.icon, jsxRuntime.jsx("span", { children: item.text })] }), item.tag && (jsxRuntime.jsx("span", { className: "absolute right-3 top-1 text-[10px] font-semibold", style: { color: item.tag.color || '#A855F7', pointerEvents: 'none' }, children: item.tag.text }))] }, index))) }))] }));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
exports.XButton = XButton;
|