发布 v1.10 #5
@@ -1,4 +1,5 @@
|
|||||||
> 1%
|
> 1%
|
||||||
last 2 versions
|
last 2 versions
|
||||||
|
Chrome >= 90
|
||||||
not dead
|
not dead
|
||||||
not ie 11
|
not ie 11
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"@codemirror/view": "^6.39.15",
|
"@codemirror/view": "^6.39.15",
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"@tinyflow-ai/vue": "workspace:*",
|
"@tinyflow-ai/vue": "workspace:*",
|
||||||
|
"@ungap/structured-clone": "1.3.0",
|
||||||
"@vueuse/core": "catalog:",
|
"@vueuse/core": "catalog:",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
@@ -53,10 +54,12 @@
|
|||||||
"vue-cropper": "^1.1.4",
|
"vue-cropper": "^1.1.4",
|
||||||
"vue-element-plus-x": "catalog:",
|
"vue-element-plus-x": "catalog:",
|
||||||
"vue-router": "catalog:",
|
"vue-router": "catalog:",
|
||||||
"vue3-json-viewer": "^2.4.1"
|
"vue3-json-viewer": "^2.4.1",
|
||||||
|
"wicg-inert": "3.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node-forge": "^1.3.14",
|
"@types/node-forge": "^1.3.14",
|
||||||
|
"@types/ungap__structured-clone": "1.2.0",
|
||||||
"cssnano": "catalog:",
|
"cssnano": "catalog:",
|
||||||
"unplugin-element-plus": "catalog:"
|
"unplugin-element-plus": "catalog:"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,6 +91,7 @@ const emit = defineEmits<{
|
|||||||
max-width: min(88%, 640px);
|
max-width: min(88%, 640px);
|
||||||
padding: 12px 14px;
|
padding: 12px 14px;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
|
background: var(--el-bg-color);
|
||||||
background: color-mix(in srgb, var(--el-bg-color) 88%, transparent);
|
background: color-mix(in srgb, var(--el-bg-color) 88%, transparent);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ function handleKeydown(event: Event | KeyboardEvent) {
|
|||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
margin: 0 16px 16px;
|
margin: 0 16px 16px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
background: color-mix(in srgb, var(--el-bg-color) 92%, transparent);
|
background: color-mix(in srgb, var(--el-bg-color) 92%, transparent);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -121,7 +121,11 @@ const sizeMap = {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="tag"
|
class="tag"
|
||||||
:class="[`tag--${size}`, { 'tag--round': round }, `tag--border-${border}`]"
|
:class="[
|
||||||
|
`tag--${size}`,
|
||||||
|
{ 'tag--closable': closable, 'tag--round': round },
|
||||||
|
`tag--border-${border}`,
|
||||||
|
]"
|
||||||
:style="[
|
:style="[
|
||||||
tagStyle,
|
tagStyle,
|
||||||
{
|
{
|
||||||
@@ -212,15 +216,15 @@ const sizeMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 为可关闭标签调整内边距 */
|
/* 为可关闭标签调整内边距 */
|
||||||
.tag:has(.tag__close) {
|
.tag--closable {
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag:has(.tag__close).tag--small {
|
.tag--closable.tag--small {
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag:has(.tag__close).tag--large {
|
.tag--closable.tag--large {
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -253,6 +253,7 @@ onMounted(() => {
|
|||||||
.tree-select {
|
.tree-select {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: var(--el-bg-color);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
color-mix(in srgb, var(--el-color-primary-light-9) 32%, var(--el-bg-color))
|
color-mix(in srgb, var(--el-color-primary-light-9) 32%, var(--el-bg-color))
|
||||||
@@ -330,6 +331,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-tree-node.is-current > .el-tree-node__content) {
|
:deep(.el-tree-node.is-current > .el-tree-node__content) {
|
||||||
|
background: var(--el-color-primary-light-8);
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary-light-8) 70%,
|
var(--el-color-primary-light-8) 70%,
|
||||||
@@ -358,6 +360,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-scrollbar__thumb) {
|
:deep(.el-scrollbar__thumb) {
|
||||||
|
background: var(--el-text-color-placeholder);
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-text-color-secondary) 22%,
|
var(--el-text-color-secondary) 22%,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable perfectionist/sort-imports -- 兼容层必须先于所有业务依赖执行。 */
|
||||||
import './polyfills';
|
import './polyfills';
|
||||||
|
|
||||||
import {initPreferences, preferences, updatePreferences,} from '@easyflow/preferences';
|
import {initPreferences, preferences, updatePreferences,} from '@easyflow/preferences';
|
||||||
|
|||||||
108
easyflow-ui-admin/app/src/polyfills.test.ts
Normal file
108
easyflow-ui-admin/app/src/polyfills.test.ts
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import {
|
||||||
|
installArrayAtPolyfill,
|
||||||
|
installObjectHasOwnPolyfill,
|
||||||
|
installPromiseWithResolversPolyfill,
|
||||||
|
installStringAtPolyfill,
|
||||||
|
installStructuredClonePolyfill,
|
||||||
|
installUrlCanParsePolyfill,
|
||||||
|
} from './polyfills';
|
||||||
|
|
||||||
|
const descriptors = new Map<
|
||||||
|
string,
|
||||||
|
[object, PropertyKey, PropertyDescriptor | undefined]
|
||||||
|
>();
|
||||||
|
|
||||||
|
function remember(key: string, target: object, property: PropertyKey) {
|
||||||
|
descriptors.set(key, [
|
||||||
|
target,
|
||||||
|
property,
|
||||||
|
Object.getOwnPropertyDescriptor(target, property),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(target: object, property: PropertyKey) {
|
||||||
|
Reflect.deleteProperty(target, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
remember('arrayAt', Array.prototype, 'at');
|
||||||
|
remember('stringAt', String.prototype, 'at');
|
||||||
|
remember('hasOwn', Object, 'hasOwn');
|
||||||
|
remember('withResolvers', Promise, 'withResolvers');
|
||||||
|
remember('structuredClone', globalThis, 'structuredClone');
|
||||||
|
remember('canParse', URL, 'canParse');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
for (const [target, property, descriptor] of descriptors.values()) {
|
||||||
|
if (descriptor) Object.defineProperty(target, property, descriptor);
|
||||||
|
else remove(target, property);
|
||||||
|
}
|
||||||
|
descriptors.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('chrome 90 browser polyfills', () => {
|
||||||
|
it('installs Array.prototype.at and String.prototype.at', () => {
|
||||||
|
remove(Array.prototype, 'at');
|
||||||
|
remove(String.prototype, 'at');
|
||||||
|
|
||||||
|
installArrayAtPolyfill();
|
||||||
|
installStringAtPolyfill();
|
||||||
|
|
||||||
|
expect(['first', 'last'].at(-1)).toBe('last');
|
||||||
|
expect('EasyFlow'.at(-1)).toBe('w');
|
||||||
|
expect('EasyFlow'.at(99)).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs Object.hasOwn without accepting inherited properties', () => {
|
||||||
|
remove(Object, 'hasOwn');
|
||||||
|
installObjectHasOwnPolyfill();
|
||||||
|
|
||||||
|
const value = Object.create({ inherited: true }) as { own?: boolean };
|
||||||
|
value.own = true;
|
||||||
|
expect(Object.hasOwn(value, 'own')).toBe(true);
|
||||||
|
expect(Object.hasOwn(value, 'inherited')).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs Promise.withResolvers and exposes working callbacks', async () => {
|
||||||
|
remove(Promise, 'withResolvers');
|
||||||
|
installPromiseWithResolversPolyfill();
|
||||||
|
|
||||||
|
// eslint-disable-next-line n/no-unsupported-features/es-syntax -- 测试目标就是验证该 API 的兼容实现。
|
||||||
|
const { promise, resolve } = Promise.withResolvers<string>();
|
||||||
|
resolve('ready');
|
||||||
|
await expect(promise).resolves.toBe('ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs structuredClone with complex value and cycle support', () => {
|
||||||
|
remove(globalThis, 'structuredClone');
|
||||||
|
installStructuredClonePolyfill();
|
||||||
|
|
||||||
|
const source: {
|
||||||
|
createdAt: Date;
|
||||||
|
lookup: Map<string, number>;
|
||||||
|
self?: unknown;
|
||||||
|
} = {
|
||||||
|
createdAt: new Date('2026-07-15T00:00:00.000Z'),
|
||||||
|
lookup: new Map([['answer', 2026]]),
|
||||||
|
};
|
||||||
|
source.self = source;
|
||||||
|
const clone = structuredClone(source);
|
||||||
|
|
||||||
|
expect(clone).not.toBe(source);
|
||||||
|
expect(clone.self).toBe(clone);
|
||||||
|
expect(clone.createdAt).toEqual(source.createdAt);
|
||||||
|
expect(clone.lookup.get('answer')).toBe(2026);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('installs URL.canParse for absolute and base-relative URLs', () => {
|
||||||
|
remove(URL, 'canParse');
|
||||||
|
installUrlCanParsePolyfill();
|
||||||
|
|
||||||
|
expect(URL.canParse('https://easyflow.example/chat')).toBe(true);
|
||||||
|
expect(URL.canParse('/chat', 'https://easyflow.example')).toBe(true);
|
||||||
|
expect(URL.canParse('/chat')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,30 +1,135 @@
|
|||||||
|
/* eslint-disable no-extend-native -- 该文件用于集中安装旧浏览器缺失的原生 API。 */
|
||||||
|
|
||||||
|
import { deserialize, serialize } from '@ungap/structured-clone';
|
||||||
|
|
||||||
|
import 'wicg-inert';
|
||||||
|
|
||||||
|
type AtFunction = (index: number) => unknown;
|
||||||
|
type StructuredCloneFunction = <T>(
|
||||||
|
value: T,
|
||||||
|
options?: StructuredSerializeOptions,
|
||||||
|
) => T;
|
||||||
|
type WithResolversResult<T> = {
|
||||||
|
promise: Promise<T>;
|
||||||
|
reject: (reason?: unknown) => void;
|
||||||
|
resolve: (value: PromiseLike<T> | T) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const objectConstructor = Object as ObjectConstructor & {
|
const objectConstructor = Object as ObjectConstructor & {
|
||||||
hasOwn?: (object: unknown, property: PropertyKey) => boolean;
|
hasOwn?: (object: unknown, property: PropertyKey) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (typeof objectConstructor.hasOwn !== 'function') {
|
|
||||||
Object.defineProperty(Object, 'hasOwn', {
|
|
||||||
configurable: true,
|
|
||||||
value(object: unknown, property: PropertyKey) {
|
|
||||||
if (object === null || object === undefined) {
|
|
||||||
throw new TypeError('Cannot convert undefined or null to object');
|
|
||||||
}
|
|
||||||
return Object.prototype.hasOwnProperty.call(Object(object), property);
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const arrayPrototype = Array.prototype as unknown[] & {
|
const arrayPrototype = Array.prototype as unknown[] & {
|
||||||
|
at?: AtFunction;
|
||||||
toReversed?: () => unknown[];
|
toReversed?: () => unknown[];
|
||||||
toSorted?: (compareFn?: (left: unknown, right: unknown) => number) => unknown[];
|
toSorted?: (
|
||||||
|
compareFn?: (left: unknown, right: unknown) => number,
|
||||||
|
) => unknown[];
|
||||||
toSpliced?: (
|
toSpliced?: (
|
||||||
start: number,
|
start: number,
|
||||||
deleteCount?: number,
|
deleteCount?: number,
|
||||||
...items: unknown[]
|
...items: unknown[]
|
||||||
) => unknown[];
|
) => unknown[];
|
||||||
};
|
};
|
||||||
|
const stringPrototype = String.prototype as string & {
|
||||||
|
at?: AtFunction;
|
||||||
|
};
|
||||||
|
const promiseConstructor = Promise as PromiseConstructor & {
|
||||||
|
withResolvers?: <T>() => WithResolversResult<T>;
|
||||||
|
};
|
||||||
|
const urlConstructor = URL as typeof URL & {
|
||||||
|
canParse?: (url: string | URL, base?: string | URL) => boolean;
|
||||||
|
};
|
||||||
|
const browserGlobal = globalThis as typeof globalThis & {
|
||||||
|
structuredClone?: StructuredCloneFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将索引转换为 Array.prototype.at 与 String.prototype.at 使用的整数。
|
||||||
|
*/
|
||||||
|
function toIntegerOrInfinity(index: number): number {
|
||||||
|
const numericIndex = Number(index);
|
||||||
|
if (Number.isNaN(numericIndex) || numericIndex === 0) return 0;
|
||||||
|
return numericIndex > 0 ? Math.floor(numericIndex) : Math.ceil(numericIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 Object.hasOwn 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installObjectHasOwnPolyfill() {
|
||||||
|
if (typeof objectConstructor.hasOwn === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(Object, 'hasOwn', {
|
||||||
|
configurable: true,
|
||||||
|
value(object: unknown, property: PropertyKey) {
|
||||||
|
if (object === null || object === undefined) {
|
||||||
|
throw new TypeError('Cannot convert undefined or null to object');
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line unicorn/new-for-builtins -- Object() 保留 ToObject 语义。
|
||||||
|
return Object.prototype.hasOwnProperty.call(Object(object), property);
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 Array.prototype.at 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installArrayAtPolyfill() {
|
||||||
|
if (typeof arrayPrototype.at === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(Array.prototype, 'at', {
|
||||||
|
configurable: true,
|
||||||
|
value(this: ArrayLike<unknown> | null | undefined, index: number): unknown {
|
||||||
|
if (this === null || this === undefined) {
|
||||||
|
throw new TypeError('Array.prototype.at called on null or undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = this as ArrayLike<unknown>;
|
||||||
|
const numericLength = Number(target.length);
|
||||||
|
const length =
|
||||||
|
Number.isNaN(numericLength) || numericLength <= 0
|
||||||
|
? 0
|
||||||
|
: Math.min(Math.floor(numericLength), Number.MAX_SAFE_INTEGER);
|
||||||
|
const relativeIndex = toIntegerOrInfinity(index);
|
||||||
|
const actualIndex =
|
||||||
|
relativeIndex >= 0 ? relativeIndex : length + relativeIndex;
|
||||||
|
|
||||||
|
if (actualIndex < 0 || actualIndex >= length) return undefined;
|
||||||
|
return target[actualIndex];
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 String.prototype.at 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installStringAtPolyfill() {
|
||||||
|
if (typeof stringPrototype.at === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(String.prototype, 'at', {
|
||||||
|
configurable: true,
|
||||||
|
value(this: unknown, index: number): string | undefined {
|
||||||
|
if (this === null || this === undefined) {
|
||||||
|
throw new TypeError('String.prototype.at called on null or undefined');
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = String(this);
|
||||||
|
const relativeIndex = toIntegerOrInfinity(index);
|
||||||
|
const actualIndex =
|
||||||
|
relativeIndex >= 0 ? relativeIndex : target.length + relativeIndex;
|
||||||
|
return actualIndex < 0 || actualIndex >= target.length
|
||||||
|
? undefined
|
||||||
|
: target[actualIndex];
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装不会修改原数组的数组复制方法。
|
||||||
|
*/
|
||||||
|
export function installArrayCopyingPolyfills() {
|
||||||
if (typeof arrayPrototype.toReversed !== 'function') {
|
if (typeof arrayPrototype.toReversed !== 'function') {
|
||||||
Object.defineProperty(Array.prototype, 'toReversed', {
|
Object.defineProperty(Array.prototype, 'toReversed', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
@@ -60,3 +165,77 @@ if (typeof arrayPrototype.toSpliced !== 'function') {
|
|||||||
writable: true,
|
writable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 Promise.withResolvers 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installPromiseWithResolversPolyfill() {
|
||||||
|
// eslint-disable-next-line n/no-unsupported-features/es-syntax -- 此处专门检测并补齐该新 API。
|
||||||
|
if (typeof promiseConstructor.withResolvers === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(Promise, 'withResolvers', {
|
||||||
|
configurable: true,
|
||||||
|
value<T>(this: PromiseConstructor): WithResolversResult<T> {
|
||||||
|
let reject!: (reason?: unknown) => void;
|
||||||
|
let resolve!: (value: PromiseLike<T> | T) => void;
|
||||||
|
const promise = new this<T>((resolvePromise, rejectPromise) => {
|
||||||
|
resolve = resolvePromise;
|
||||||
|
reject = rejectPromise;
|
||||||
|
});
|
||||||
|
return { promise, reject, resolve };
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装可处理循环引用、Map、Set 与日期的 structuredClone 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installStructuredClonePolyfill() {
|
||||||
|
if (typeof browserGlobal.structuredClone === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(globalThis, 'structuredClone', {
|
||||||
|
configurable: true,
|
||||||
|
value: <T>(value: T) => deserialize(serialize(value)) as T,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装 URL.canParse 兼容实现。
|
||||||
|
*/
|
||||||
|
export function installUrlCanParsePolyfill() {
|
||||||
|
if (typeof urlConstructor.canParse === 'function') return;
|
||||||
|
|
||||||
|
Object.defineProperty(URL, 'canParse', {
|
||||||
|
configurable: true,
|
||||||
|
value(url: string | URL, base?: string | URL) {
|
||||||
|
try {
|
||||||
|
const parsedUrl =
|
||||||
|
arguments.length > 1
|
||||||
|
? new URL(String(url), String(base))
|
||||||
|
: new URL(String(url));
|
||||||
|
return parsedUrl instanceof URL;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在业务模块加载前安装 Chrome 90 缺失的运行时能力。
|
||||||
|
*/
|
||||||
|
export function installLegacyBrowserPolyfills() {
|
||||||
|
installObjectHasOwnPolyfill();
|
||||||
|
installArrayAtPolyfill();
|
||||||
|
installStringAtPolyfill();
|
||||||
|
installArrayCopyingPolyfills();
|
||||||
|
installPromiseWithResolversPolyfill();
|
||||||
|
installStructuredClonePolyfill();
|
||||||
|
installUrlCanParsePolyfill();
|
||||||
|
}
|
||||||
|
|
||||||
|
installLegacyBrowserPolyfills();
|
||||||
|
|||||||
1
easyflow-ui-admin/app/src/typings/wicg-inert.d.ts
vendored
Normal file
1
easyflow-ui-admin/app/src/typings/wicg-inert.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
declare module 'wicg-inert';
|
||||||
@@ -181,6 +181,7 @@ const selectedToolOptions = computed(() => {
|
|||||||
width: min(420px, calc(100vw - 320px));
|
width: min(420px, calc(100vw - 320px));
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: var(--el-bg-color);
|
||||||
background: color-mix(in srgb, var(--el-bg-color) 94%, transparent);
|
background: color-mix(in srgb, var(--el-bg-color) 94%, transparent);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { computed, ref, watch } from 'vue';
|
import { computed, ref, watch } from 'vue';
|
||||||
|
|
||||||
import { ArrowRight } from '@element-plus/icons-vue';
|
import { ArrowRight } from '@element-plus/icons-vue';
|
||||||
|
import { useResizeObserver } from '@vueuse/core';
|
||||||
import { ElIcon } from 'element-plus';
|
import { ElIcon } from 'element-plus';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -17,8 +18,17 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const avatarFailed = ref(false);
|
const avatarFailed = ref(false);
|
||||||
|
const welcomeRef = ref<HTMLElement>();
|
||||||
|
const legacyCompact = ref(false);
|
||||||
const headingId = `agent-welcome-${Math.random().toString(36).slice(2, 9)}`;
|
const headingId = `agent-welcome-${Math.random().toString(36).slice(2, 9)}`;
|
||||||
const initial = computed(() => [...props.agentName.trim()][0] || 'A');
|
const initial = computed(() => [...props.agentName.trim()][0] || 'A');
|
||||||
|
const needsContainerQueryFallback =
|
||||||
|
typeof CSS === 'undefined' || !CSS.supports('container-type', 'inline-size');
|
||||||
|
|
||||||
|
useResizeObserver(welcomeRef, ([entry]) => {
|
||||||
|
if (!needsContainerQueryFallback || !entry) return;
|
||||||
|
legacyCompact.value = entry.contentRect.width <= 560;
|
||||||
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.avatar,
|
() => props.avatar,
|
||||||
@@ -29,7 +39,12 @@ watch(
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<section class="agent-welcome" :aria-labelledby="headingId">
|
<section
|
||||||
|
ref="welcomeRef"
|
||||||
|
class="agent-welcome"
|
||||||
|
:class="{ 'is-legacy-compact': legacyCompact }"
|
||||||
|
:aria-labelledby="headingId"
|
||||||
|
>
|
||||||
<div class="agent-welcome__content">
|
<div class="agent-welcome__content">
|
||||||
<div class="agent-welcome__avatar" aria-hidden="true">
|
<div class="agent-welcome__avatar" aria-hidden="true">
|
||||||
<img
|
<img
|
||||||
@@ -213,6 +228,15 @@ watch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.agent-welcome.is-legacy-compact .agent-welcome__suggestions-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-welcome.is-legacy-compact .agent-welcome__message {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.agent-welcome__question,
|
.agent-welcome__question,
|
||||||
.agent-welcome__question .el-icon {
|
.agent-welcome__question .el-icon {
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.agent-studio-canvas :deep(.tf-flow-line-path) {
|
.agent-studio-canvas :deep(.tf-flow-line-path) {
|
||||||
|
stroke: var(--el-border-color);
|
||||||
stroke: color-mix(
|
stroke: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary) 30%,
|
var(--el-color-primary) 30%,
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ defineProps<{
|
|||||||
|
|
||||||
.agent-studio-edge-layer__path {
|
.agent-studio-edge-layer__path {
|
||||||
fill: none;
|
fill: none;
|
||||||
|
stroke: var(--el-border-color);
|
||||||
stroke: color-mix(
|
stroke: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary) 30%,
|
var(--el-color-primary) 30%,
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ const iconComponent = computed(() => {
|
|||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
background: var(--el-bg-color);
|
||||||
background: color-mix(in srgb, var(--el-bg-color) 96%, transparent);
|
background: color-mix(in srgb, var(--el-bg-color) 96%, transparent);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
@@ -73,6 +74,7 @@ const iconComponent = computed(() => {
|
|||||||
|
|
||||||
.agent-studio-node:hover {
|
.agent-studio-node:hover {
|
||||||
background: var(--el-bg-color);
|
background: var(--el-bg-color);
|
||||||
|
border-color: var(--el-color-primary-light-7);
|
||||||
border-color: color-mix(
|
border-color: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary) 26%,
|
var(--el-color-primary) 26%,
|
||||||
@@ -88,11 +90,13 @@ const iconComponent = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.agent-studio-node.is-selected {
|
.agent-studio-node.is-selected {
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary-light-9) 54%,
|
var(--el-color-primary-light-9) 54%,
|
||||||
var(--el-bg-color)
|
var(--el-bg-color)
|
||||||
);
|
);
|
||||||
|
border-color: var(--el-color-primary-light-5);
|
||||||
border-color: color-mix(
|
border-color: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary) 42%,
|
var(--el-color-primary) 42%,
|
||||||
@@ -132,6 +136,7 @@ const iconComponent = computed(() => {
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
|
background: var(--el-color-primary-light-8);
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary-light-8) 74%,
|
var(--el-color-primary-light-8) 74%,
|
||||||
@@ -174,6 +179,7 @@ const iconComponent = computed(() => {
|
|||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
line-height: 16px;
|
line-height: 16px;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
background: color-mix(
|
background: color-mix(
|
||||||
in srgb,
|
in srgb,
|
||||||
var(--el-color-primary-light-9) 74%,
|
var(--el-color-primary-light-9) 74%,
|
||||||
@@ -193,4 +199,14 @@ const iconComponent = computed(() => {
|
|||||||
color: var(--el-text-color-regular);
|
color: var(--el-text-color-regular);
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (color: color-mix(in srgb, red, blue)) {
|
||||||
|
.agent-studio-node:hover {
|
||||||
|
border-color: var(--el-color-primary-light-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.agent-studio-node.is-selected {
|
||||||
|
border-color: var(--el-color-primary-light-5);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -357,11 +357,13 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
position: relative;
|
position: relative;
|
||||||
padding: 20px 20px 18px;
|
padding: 20px 20px 18px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: var(--el-fill-color-blank);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
color-mix(in srgb, var(--el-color-primary-light-9) 80%, white) 0%,
|
color-mix(in srgb, var(--el-color-primary-light-9) 80%, white) 0%,
|
||||||
var(--el-fill-color-blank) 38%
|
var(--el-fill-color-blank) 38%
|
||||||
);
|
);
|
||||||
|
border: 1px solid var(--el-border-color-light);
|
||||||
border: 1px solid color-mix(in srgb, var(--el-border-color-light) 78%, white);
|
border: 1px solid color-mix(in srgb, var(--el-border-color-light) 78%, white);
|
||||||
border-radius: 18px;
|
border-radius: 18px;
|
||||||
box-shadow: 0 18px 40px rgb(15 23 42 / 6%);
|
box-shadow: 0 18px 40px rgb(15 23 42 / 6%);
|
||||||
@@ -372,6 +374,7 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
inset: 0 auto 0 0;
|
inset: 0 auto 0 0;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
content: '';
|
content: '';
|
||||||
|
background: var(--el-color-primary);
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
var(--el-color-primary),
|
var(--el-color-primary),
|
||||||
@@ -393,7 +396,9 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
width: 34px;
|
width: 34px;
|
||||||
height: 34px;
|
height: 34px;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
background: color-mix(in srgb, var(--el-color-primary-light-9) 66%, white);
|
background: color-mix(in srgb, var(--el-color-primary-light-9) 66%, white);
|
||||||
|
border: 1px solid var(--el-color-primary-light-8);
|
||||||
border: 1px solid
|
border: 1px solid
|
||||||
color-mix(in srgb, var(--el-color-primary-light-8) 72%, white);
|
color-mix(in srgb, var(--el-color-primary-light-8) 72%, white);
|
||||||
box-shadow: 0 8px 18px rgb(37 99 235 / 10%);
|
box-shadow: 0 8px 18px rgb(37 99 235 / 10%);
|
||||||
@@ -402,6 +407,7 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
.chunk-card__action--ghost {
|
.chunk-card__action--ghost {
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
background: rgb(255 255 255 / 86%);
|
background: rgb(255 255 255 / 86%);
|
||||||
|
border-color: var(--el-border-color-light);
|
||||||
border-color: color-mix(in srgb, var(--el-border-color-light) 86%, white);
|
border-color: color-mix(in srgb, var(--el-border-color-light) 86%, white);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
@@ -429,6 +435,7 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
height: 8px;
|
height: 8px;
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
background: var(--el-color-primary);
|
background: var(--el-color-primary);
|
||||||
|
box-shadow: 0 0 0 4px var(--el-color-primary-light-9);
|
||||||
box-shadow: 0 0 0 4px
|
box-shadow: 0 0 0 4px
|
||||||
color-mix(in srgb, var(--el-color-primary-light-8) 50%, transparent);
|
color-mix(in srgb, var(--el-color-primary-light-8) 50%, transparent);
|
||||||
}
|
}
|
||||||
@@ -594,4 +601,18 @@ const getChunkHeaderLabel = (row: any) => {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (color: color-mix(in srgb, red, blue)) {
|
||||||
|
.chunk-card {
|
||||||
|
border-color: var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chunk-card__action {
|
||||||
|
border-color: var(--el-color-primary-light-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chunk-card__action--ghost {
|
||||||
|
border-color: var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -492,7 +492,9 @@ watch(
|
|||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
color: var(--el-color-danger-dark-2);
|
color: var(--el-color-danger-dark-2);
|
||||||
|
background: var(--el-color-danger-light-9);
|
||||||
background: color-mix(in srgb, var(--el-color-danger-light-9) 88%, white);
|
background: color-mix(in srgb, var(--el-color-danger-light-9) 88%, white);
|
||||||
|
border: 1px solid var(--el-color-danger-light-8);
|
||||||
border: 1px solid color-mix(in srgb, var(--el-color-danger) 14%, white);
|
border: 1px solid color-mix(in srgb, var(--el-color-danger) 14%, white);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
}
|
}
|
||||||
@@ -554,4 +556,10 @@ watch(
|
|||||||
min-height: auto;
|
min-height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (color: color-mix(in srgb, red, blue)) {
|
||||||
|
.workbench__error {
|
||||||
|
border-color: var(--el-color-danger-light-8);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ const isActiveChunk = (chunk: ChunkItem) =>
|
|||||||
padding: 15px 16px 14px;
|
padding: 15px 16px 14px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
background: var(--el-fill-color-blank);
|
||||||
background: color-mix(in srgb, var(--el-fill-color-blank) 92%, white);
|
background: color-mix(in srgb, var(--el-fill-color-blank) 92%, white);
|
||||||
border: 1px solid rgb(15 23 42 / 7%);
|
border: 1px solid rgb(15 23 42 / 7%);
|
||||||
border-radius: 14px;
|
border-radius: 14px;
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const props = defineProps<{
|
|||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5 [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5',
|
'relative [&>[data-slot=input]]:pl-5 [&>[data-slot=input]]:pr-5',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -201,8 +201,11 @@ function toggleCitation(id: string) {
|
|||||||
min-height: 26px;
|
min-height: 26px;
|
||||||
padding: 3px 9px;
|
padding: 3px 9px;
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
background: color-mix(in srgb, var(--el-color-primary-light-9) 78%, white);
|
background: color-mix(in srgb, var(--el-color-primary-light-9) 78%, white);
|
||||||
border: 1px solid color-mix(in srgb, var(--el-color-primary-light-7) 72%, white);
|
border: 1px solid var(--el-color-primary-light-7);
|
||||||
|
border: 1px solid
|
||||||
|
color-mix(in srgb, var(--el-color-primary-light-7) 72%, white);
|
||||||
border-radius: 999px;
|
border-radius: 999px;
|
||||||
transition:
|
transition:
|
||||||
color 0.18s ease,
|
color 0.18s ease,
|
||||||
@@ -215,6 +218,7 @@ function toggleCitation(id: string) {
|
|||||||
.chat-knowledge-card__pill:hover,
|
.chat-knowledge-card__pill:hover,
|
||||||
.chat-knowledge-card__pill.is-active {
|
.chat-knowledge-card__pill.is-active {
|
||||||
color: var(--el-color-primary);
|
color: var(--el-color-primary);
|
||||||
|
background: var(--el-color-primary-light-8);
|
||||||
background: color-mix(in srgb, var(--el-color-primary-light-8) 80%, white);
|
background: color-mix(in srgb, var(--el-color-primary-light-8) 80%, white);
|
||||||
border-color: var(--el-color-primary-light-5);
|
border-color: var(--el-color-primary-light-5);
|
||||||
}
|
}
|
||||||
@@ -236,7 +240,12 @@ function toggleCitation(id: string) {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
color: color-mix(in srgb, var(--el-color-primary) 78%, var(--el-text-color-regular));
|
color: var(--el-color-primary);
|
||||||
|
color: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--el-color-primary) 78%,
|
||||||
|
var(--el-text-color-regular)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-knowledge-card__label {
|
.chat-knowledge-card__label {
|
||||||
@@ -253,7 +262,12 @@ function toggleCitation(id: string) {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: color-mix(in srgb, var(--el-color-primary) 62%, var(--el-text-color-secondary));
|
color: var(--el-text-color-secondary);
|
||||||
|
color: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--el-color-primary) 62%,
|
||||||
|
var(--el-text-color-secondary)
|
||||||
|
);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,6 +276,7 @@ function toggleCitation(id: string) {
|
|||||||
padding: 14px;
|
padding: 14px;
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
color: var(--el-text-color-primary);
|
color: var(--el-text-color-primary);
|
||||||
|
background: var(--el-bg-color-overlay);
|
||||||
background: color-mix(in srgb, var(--el-bg-color-overlay) 94%, white);
|
background: color-mix(in srgb, var(--el-bg-color-overlay) 94%, white);
|
||||||
border: 1px solid var(--el-border-color-lighter);
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
@@ -335,6 +350,7 @@ function toggleCitation(id: string) {
|
|||||||
|
|
||||||
.chat-knowledge-card__hit {
|
.chat-knowledge-card__hit {
|
||||||
padding: 10px 10px 10px 12px;
|
padding: 10px 10px 10px 12px;
|
||||||
|
background: var(--el-fill-color-lighter);
|
||||||
background: color-mix(in srgb, var(--el-fill-color-lighter) 72%, white);
|
background: color-mix(in srgb, var(--el-fill-color-lighter) 72%, white);
|
||||||
border-left: 2px solid var(--el-color-primary-light-5);
|
border-left: 2px solid var(--el-color-primary-light-5);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -365,4 +381,10 @@ function toggleCitation(id: string) {
|
|||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@supports not (color: color-mix(in srgb, red, blue)) {
|
||||||
|
.chat-knowledge-card__pill {
|
||||||
|
border-color: var(--el-color-primary-light-7);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
padding: 0.6em 0;
|
padding: 0.6em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vxe-tools--operate:empty,
|
||||||
.vxe-tools--operate:not(:has(button)) {
|
.vxe-tools--operate:not(:has(button)) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#snippet floating()}
|
{#snippet floating()}
|
||||||
<div class="tf-mixed-dropdown nopan nodrag nowheel">
|
<div class="tf-mixed-dropdown nopan nodrag nowheel {hoveredItem?.children?.length ? 'has-secondary' : ''}">
|
||||||
<div class="tf-mixed-list tf-mixed-primary-list">
|
<div class="tf-mixed-list tf-mixed-primary-list">
|
||||||
{#each refOptions as item}
|
{#each refOptions as item}
|
||||||
<button class="tf-mixed-item {hoveredItem?.value === item.value ? 'active' : ''}"
|
<button class="tf-mixed-item {hoveredItem?.value === item.value ? 'active' : ''}"
|
||||||
@@ -391,7 +391,7 @@
|
|||||||
}
|
}
|
||||||
.tf-mixed-list { display: flex; flex-direction: column; padding: 8px; overflow-y: auto; }
|
.tf-mixed-list { display: flex; flex-direction: column; padding: 8px; overflow-y: auto; }
|
||||||
.tf-mixed-primary-list { width: 100%; flex-shrink: 0; background: var(--tf-bg-surface-alt); }
|
.tf-mixed-primary-list { width: 100%; flex-shrink: 0; background: var(--tf-bg-surface-alt); }
|
||||||
.tf-mixed-dropdown:has(.tf-mixed-secondary-list) .tf-mixed-primary-list { width: auto; min-width: 180px; }
|
.tf-mixed-dropdown.has-secondary .tf-mixed-primary-list { width: auto; min-width: 180px; }
|
||||||
.tf-mixed-secondary-list { min-width: 220px; background: var(--tf-bg-surface); padding: 12px; border-left: 1px solid var(--tf-bg-muted); animation: slideIn 0.2s ease-out; box-sizing: border-box; }
|
.tf-mixed-secondary-list { min-width: 220px; background: var(--tf-bg-surface); padding: 12px; border-left: 1px solid var(--tf-bg-muted); animation: slideIn 0.2s ease-out; box-sizing: border-box; }
|
||||||
|
|
||||||
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
|
@keyframes slideIn { from { opacity: 0; transform: translateX(-10px); } to { opacity: 1; transform: translateX(0); } }
|
||||||
|
|||||||
@@ -296,7 +296,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="tf-select-wrapper nopan nodrag nowheel">
|
<div class="tf-select-wrapper nopan nodrag nowheel {hoveredItem?.children?.length ? 'has-secondary' : ''}">
|
||||||
<!-- 一级列表:节点/分类 -->
|
<!-- 一级列表:节点/分类 -->
|
||||||
<div class="tf-select-list tf-select-primary-list">
|
<div class="tf-select-list tf-select-primary-list">
|
||||||
{#each items as item}
|
{#each items as item}
|
||||||
@@ -580,7 +580,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* When a secondary list is open, we fix the primary list to 100% of the input's width (done via absolute positioning or just keeping it wide enough) */
|
/* When a secondary list is open, we fix the primary list to 100% of the input's width (done via absolute positioning or just keeping it wide enough) */
|
||||||
.tf-select-wrapper:has(.tf-select-secondary-list) &.tf-select-primary-list {
|
.tf-select-wrapper.has-secondary &.tf-select-primary-list {
|
||||||
/* Let it take the width of the input minus borders/paddings if needed, but minWidth handles it mostly */
|
/* Let it take the width of the input minus borders/paddings if needed, but minWidth handles it mostly */
|
||||||
width: auto;
|
width: auto;
|
||||||
min-width: 160px;
|
min-width: 160px;
|
||||||
|
|||||||
19
easyflow-ui-admin/pnpm-lock.yaml
generated
19
easyflow-ui-admin/pnpm-lock.yaml
generated
@@ -661,6 +661,9 @@ importers:
|
|||||||
'@tinyflow-ai/vue':
|
'@tinyflow-ai/vue':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../packages/tinyflow-vue
|
version: link:../packages/tinyflow-vue
|
||||||
|
'@ungap/structured-clone':
|
||||||
|
specifier: 1.3.0
|
||||||
|
version: 1.3.0
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 13.9.0(vue@3.5.24(typescript@5.9.3))
|
version: 13.9.0(vue@3.5.24(typescript@5.9.3))
|
||||||
@@ -712,10 +715,16 @@ importers:
|
|||||||
vue3-json-viewer:
|
vue3-json-viewer:
|
||||||
specifier: ^2.4.1
|
specifier: ^2.4.1
|
||||||
version: 2.4.1(vue@3.5.24(typescript@5.9.3))
|
version: 2.4.1(vue@3.5.24(typescript@5.9.3))
|
||||||
|
wicg-inert:
|
||||||
|
specifier: 3.1.3
|
||||||
|
version: 3.1.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node-forge':
|
'@types/node-forge':
|
||||||
specifier: ^1.3.14
|
specifier: ^1.3.14
|
||||||
version: 1.3.14
|
version: 1.3.14
|
||||||
|
'@types/ungap__structured-clone':
|
||||||
|
specifier: 1.2.0
|
||||||
|
version: 1.2.0
|
||||||
cssnano:
|
cssnano:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 7.1.2(postcss@8.5.6)
|
version: 7.1.2(postcss@8.5.6)
|
||||||
@@ -4360,6 +4369,9 @@ packages:
|
|||||||
'@types/trusted-types@2.0.7':
|
'@types/trusted-types@2.0.7':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
|
'@types/ungap__structured-clone@1.2.0':
|
||||||
|
resolution: {integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==}
|
||||||
|
|
||||||
'@types/unist@2.0.11':
|
'@types/unist@2.0.11':
|
||||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||||
|
|
||||||
@@ -10982,6 +10994,9 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
wicg-inert@3.1.3:
|
||||||
|
resolution: {integrity: sha512-5L0PKK7iP+0Q/jv2ccgmkz/pfXbumZtlEyWS/xnX+L+Og3f7WjL4+iEs18k4IuldOX3PgGpza3qGndL9xUBjCQ==}
|
||||||
|
|
||||||
widest-line@5.0.0:
|
widest-line@5.0.0:
|
||||||
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
|
resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -14195,6 +14210,8 @@ snapshots:
|
|||||||
|
|
||||||
'@types/trusted-types@2.0.7': {}
|
'@types/trusted-types@2.0.7': {}
|
||||||
|
|
||||||
|
'@types/ungap__structured-clone@1.2.0': {}
|
||||||
|
|
||||||
'@types/unist@2.0.11': {}
|
'@types/unist@2.0.11': {}
|
||||||
|
|
||||||
'@types/unist@3.0.3': {}
|
'@types/unist@3.0.3': {}
|
||||||
@@ -21789,6 +21806,8 @@ snapshots:
|
|||||||
siginfo: 2.0.0
|
siginfo: 2.0.0
|
||||||
stackback: 0.0.2
|
stackback: 0.0.2
|
||||||
|
|
||||||
|
wicg-inert@3.1.3: {}
|
||||||
|
|
||||||
widest-line@5.0.0:
|
widest-line@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
string-width: 7.2.0
|
string-width: 7.2.0
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
> 1%
|
> 1%
|
||||||
last 2 versions
|
last 2 versions
|
||||||
|
Chrome >= 90
|
||||||
not dead
|
not dead
|
||||||
not ie 11
|
not ie 11
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
"@easyflow/types": "workspace:*",
|
"@easyflow/types": "workspace:*",
|
||||||
"@easyflow/utils": "workspace:*",
|
"@easyflow/utils": "workspace:*",
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
|
"@ungap/structured-clone": "1.3.0",
|
||||||
"@vueuse/core": "catalog:",
|
"@vueuse/core": "catalog:",
|
||||||
"dayjs": "catalog:",
|
"dayjs": "catalog:",
|
||||||
"element-plus": "catalog:",
|
"element-plus": "catalog:",
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node-forge": "^1.3.14",
|
"@types/node-forge": "^1.3.14",
|
||||||
|
"@types/ungap__structured-clone": "1.2.0",
|
||||||
"unplugin-element-plus": "catalog:"
|
"unplugin-element-plus": "catalog:"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,7 +121,11 @@ const sizeMap = {
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="tag"
|
class="tag"
|
||||||
:class="[`tag--${size}`, { 'tag--round': round }, `tag--border-${border}`]"
|
:class="[
|
||||||
|
`tag--${size}`,
|
||||||
|
{ 'tag--closable': closable, 'tag--round': round },
|
||||||
|
`tag--border-${border}`,
|
||||||
|
]"
|
||||||
:style="[
|
:style="[
|
||||||
tagStyle,
|
tagStyle,
|
||||||
{
|
{
|
||||||
@@ -212,15 +216,15 @@ const sizeMap = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 为可关闭标签调整内边距 */
|
/* 为可关闭标签调整内边距 */
|
||||||
.tag:has(.tag__close) {
|
.tag--closable {
|
||||||
padding-right: 8px;
|
padding-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag:has(.tag__close).tag--small {
|
.tag--closable.tag--small {
|
||||||
padding-right: 6px;
|
padding-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag:has(.tag__close).tag--large {
|
.tag--closable.tag--large {
|
||||||
padding-right: 12px;
|
padding-right: 12px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
/* eslint-disable perfectionist/sort-imports -- 兼容层必须先于所有业务依赖执行。 */
|
||||||
|
import './polyfills';
|
||||||
|
|
||||||
import { initPreferences } from '@easyflow/preferences';
|
import { initPreferences } from '@easyflow/preferences';
|
||||||
import { unmountGlobalLoading } from '@easyflow/utils';
|
import { unmountGlobalLoading } from '@easyflow/utils';
|
||||||
|
|
||||||
|
|||||||
82
easyflow-ui-usercenter/app/src/polyfills.test.ts
Normal file
82
easyflow-ui-usercenter/app/src/polyfills.test.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import {
|
||||||
|
installArrayAtPolyfill,
|
||||||
|
installObjectHasOwnPolyfill,
|
||||||
|
installPromiseWithResolversPolyfill,
|
||||||
|
installStringAtPolyfill,
|
||||||
|
installStructuredClonePolyfill,
|
||||||
|
installUrlCanParsePolyfill,
|
||||||
|
} from './polyfills';
|
||||||
|
|
||||||
|
const descriptors = new Map<
|
||||||
|
string,
|
||||||
|
[object, PropertyKey, PropertyDescriptor | undefined]
|
||||||
|
>();
|
||||||
|
|
||||||
|
function remember(key: string, target: object, property: PropertyKey) {
|
||||||
|
descriptors.set(key, [
|
||||||
|
target,
|
||||||
|
property,
|
||||||
|
Object.getOwnPropertyDescriptor(target, property),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(target: object, property: PropertyKey) {
|
||||||
|
Reflect.deleteProperty(target, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
remember('arrayAt', Array.prototype, 'at');
|
||||||
|
remember('stringAt', String.prototype, 'at');
|
||||||
|
remember('hasOwn', Object, 'hasOwn');
|
||||||
|
remember('withResolvers', Promise, 'withResolvers');
|
||||||
|
remember('structuredClone', globalThis, 'structuredClone');
|
||||||
|
remember('canParse', URL, 'canParse');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
for (const [target, property, descriptor] of descriptors.values()) {
|
||||||
|
if (descriptor) Object.defineProperty(target, property, descriptor);
|
||||||
|
else remove(target, property);
|
||||||
|
}
|
||||||
|
descriptors.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('chrome 90 browser polyfills', () => {
|
||||||
|
it('restores missing collection and object APIs', () => {
|
||||||
|
remove(Array.prototype, 'at');
|
||||||
|
remove(String.prototype, 'at');
|
||||||
|
remove(Object, 'hasOwn');
|
||||||
|
installArrayAtPolyfill();
|
||||||
|
installStringAtPolyfill();
|
||||||
|
installObjectHasOwnPolyfill();
|
||||||
|
|
||||||
|
expect(['first', 'last'].at(-1)).toBe('last');
|
||||||
|
expect('EasyFlow'.at(-1)).toBe('w');
|
||||||
|
expect(Object.hasOwn({ ready: true }, 'ready')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('restores missing async, clone and URL APIs', async () => {
|
||||||
|
remove(Promise, 'withResolvers');
|
||||||
|
remove(globalThis, 'structuredClone');
|
||||||
|
remove(URL, 'canParse');
|
||||||
|
installPromiseWithResolversPolyfill();
|
||||||
|
installStructuredClonePolyfill();
|
||||||
|
installUrlCanParsePolyfill();
|
||||||
|
|
||||||
|
// eslint-disable-next-line n/no-unsupported-features/es-syntax -- 测试目标就是验证该 API 的兼容实现。
|
||||||
|
const { promise, resolve } = Promise.withResolvers<string>();
|
||||||
|
resolve('ready');
|
||||||
|
await expect(promise).resolves.toBe('ready');
|
||||||
|
|
||||||
|
const source: { map: Map<string, number>; self?: unknown } = {
|
||||||
|
map: new Map([['answer', 2026]]),
|
||||||
|
};
|
||||||
|
source.self = source;
|
||||||
|
const clone = structuredClone(source);
|
||||||
|
expect(clone.self).toBe(clone);
|
||||||
|
expect(clone.map.get('answer')).toBe(2026);
|
||||||
|
expect(URL.canParse('/chat', 'https://easyflow.example')).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
208
easyflow-ui-usercenter/app/src/polyfills.ts
Normal file
208
easyflow-ui-usercenter/app/src/polyfills.ts
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
/* eslint-disable no-extend-native -- 该文件用于集中安装旧浏览器缺失的原生 API。 */
|
||||||
|
|
||||||
|
import { deserialize, serialize } from '@ungap/structured-clone';
|
||||||
|
|
||||||
|
type AtFunction = (index: number) => unknown;
|
||||||
|
type StructuredCloneFunction = <T>(
|
||||||
|
value: T,
|
||||||
|
options?: StructuredSerializeOptions,
|
||||||
|
) => T;
|
||||||
|
type WithResolversResult<T> = {
|
||||||
|
promise: Promise<T>;
|
||||||
|
reject: (reason?: unknown) => void;
|
||||||
|
resolve: (value: PromiseLike<T> | T) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
const objectConstructor = Object as ObjectConstructor & {
|
||||||
|
hasOwn?: (object: unknown, property: PropertyKey) => boolean;
|
||||||
|
};
|
||||||
|
const arrayPrototype = Array.prototype as unknown[] & {
|
||||||
|
at?: AtFunction;
|
||||||
|
toReversed?: () => unknown[];
|
||||||
|
toSorted?: (
|
||||||
|
compareFn?: (left: unknown, right: unknown) => number,
|
||||||
|
) => unknown[];
|
||||||
|
toSpliced?: (
|
||||||
|
start: number,
|
||||||
|
deleteCount?: number,
|
||||||
|
...items: unknown[]
|
||||||
|
) => unknown[];
|
||||||
|
};
|
||||||
|
const stringPrototype = String.prototype as string & {
|
||||||
|
at?: AtFunction;
|
||||||
|
};
|
||||||
|
const promiseConstructor = Promise as PromiseConstructor & {
|
||||||
|
withResolvers?: <T>() => WithResolversResult<T>;
|
||||||
|
};
|
||||||
|
const urlConstructor = URL as typeof URL & {
|
||||||
|
canParse?: (url: string | URL, base?: string | URL) => boolean;
|
||||||
|
};
|
||||||
|
const browserGlobal = globalThis as typeof globalThis & {
|
||||||
|
structuredClone?: StructuredCloneFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 将索引转换为 at 方法使用的整数。 */
|
||||||
|
function toIntegerOrInfinity(index: number): number {
|
||||||
|
const numericIndex = Number(index);
|
||||||
|
if (Number.isNaN(numericIndex) || numericIndex === 0) return 0;
|
||||||
|
return numericIndex > 0 ? Math.floor(numericIndex) : Math.ceil(numericIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装 Object.hasOwn 兼容实现。 */
|
||||||
|
export function installObjectHasOwnPolyfill() {
|
||||||
|
if (typeof objectConstructor.hasOwn === 'function') return;
|
||||||
|
Object.defineProperty(Object, 'hasOwn', {
|
||||||
|
configurable: true,
|
||||||
|
value(object: unknown, property: PropertyKey) {
|
||||||
|
if (object === null || object === undefined) {
|
||||||
|
throw new TypeError('Cannot convert undefined or null to object');
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line unicorn/new-for-builtins -- Object() 保留 ToObject 语义。
|
||||||
|
return Object.prototype.hasOwnProperty.call(Object(object), property);
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装 Array.prototype.at 兼容实现。 */
|
||||||
|
export function installArrayAtPolyfill() {
|
||||||
|
if (typeof arrayPrototype.at === 'function') return;
|
||||||
|
Object.defineProperty(Array.prototype, 'at', {
|
||||||
|
configurable: true,
|
||||||
|
value(this: ArrayLike<unknown> | null | undefined, index: number): unknown {
|
||||||
|
if (this === null || this === undefined) {
|
||||||
|
throw new TypeError('Array.prototype.at called on null or undefined');
|
||||||
|
}
|
||||||
|
const target = this as ArrayLike<unknown>;
|
||||||
|
const numericLength = Number(target.length);
|
||||||
|
const length =
|
||||||
|
Number.isNaN(numericLength) || numericLength <= 0
|
||||||
|
? 0
|
||||||
|
: Math.min(Math.floor(numericLength), Number.MAX_SAFE_INTEGER);
|
||||||
|
const relativeIndex = toIntegerOrInfinity(index);
|
||||||
|
const actualIndex =
|
||||||
|
relativeIndex >= 0 ? relativeIndex : length + relativeIndex;
|
||||||
|
return actualIndex < 0 || actualIndex >= length
|
||||||
|
? undefined
|
||||||
|
: target[actualIndex];
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装 String.prototype.at 兼容实现。 */
|
||||||
|
export function installStringAtPolyfill() {
|
||||||
|
if (typeof stringPrototype.at === 'function') return;
|
||||||
|
Object.defineProperty(String.prototype, 'at', {
|
||||||
|
configurable: true,
|
||||||
|
value(this: unknown, index: number): string | undefined {
|
||||||
|
if (this === null || this === undefined) {
|
||||||
|
throw new TypeError('String.prototype.at called on null or undefined');
|
||||||
|
}
|
||||||
|
const target = String(this);
|
||||||
|
const relativeIndex = toIntegerOrInfinity(index);
|
||||||
|
const actualIndex =
|
||||||
|
relativeIndex >= 0 ? relativeIndex : target.length + relativeIndex;
|
||||||
|
return actualIndex < 0 || actualIndex >= target.length
|
||||||
|
? undefined
|
||||||
|
: target[actualIndex];
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装不会修改原数组的数组复制方法。 */
|
||||||
|
export function installArrayCopyingPolyfills() {
|
||||||
|
if (typeof arrayPrototype.toReversed !== 'function') {
|
||||||
|
Object.defineProperty(Array.prototype, 'toReversed', {
|
||||||
|
configurable: true,
|
||||||
|
value() {
|
||||||
|
return Array.prototype.slice.call(this).reverse();
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof arrayPrototype.toSorted !== 'function') {
|
||||||
|
Object.defineProperty(Array.prototype, 'toSorted', {
|
||||||
|
configurable: true,
|
||||||
|
value(compareFn?: (left: unknown, right: unknown) => number) {
|
||||||
|
return Array.prototype.slice.call(this).sort(compareFn);
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (typeof arrayPrototype.toSpliced !== 'function') {
|
||||||
|
Object.defineProperty(Array.prototype, 'toSpliced', {
|
||||||
|
configurable: true,
|
||||||
|
value(start: number, deleteCount?: number, ...items: unknown[]) {
|
||||||
|
const copy = Array.prototype.slice.call(this);
|
||||||
|
if (arguments.length === 1) copy.splice(start);
|
||||||
|
else copy.splice(start, deleteCount as number, ...items);
|
||||||
|
return copy;
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装 Promise.withResolvers 兼容实现。 */
|
||||||
|
export function installPromiseWithResolversPolyfill() {
|
||||||
|
// eslint-disable-next-line n/no-unsupported-features/es-syntax -- 此处专门检测并补齐该新 API。
|
||||||
|
if (typeof promiseConstructor.withResolvers === 'function') return;
|
||||||
|
Object.defineProperty(Promise, 'withResolvers', {
|
||||||
|
configurable: true,
|
||||||
|
value<T>(this: PromiseConstructor): WithResolversResult<T> {
|
||||||
|
let reject!: (reason?: unknown) => void;
|
||||||
|
let resolve!: (value: PromiseLike<T> | T) => void;
|
||||||
|
const promise = new this<T>((resolvePromise, rejectPromise) => {
|
||||||
|
resolve = resolvePromise;
|
||||||
|
reject = rejectPromise;
|
||||||
|
});
|
||||||
|
return { promise, reject, resolve };
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装可处理复杂值与循环引用的 structuredClone 兼容实现。 */
|
||||||
|
export function installStructuredClonePolyfill() {
|
||||||
|
if (typeof browserGlobal.structuredClone === 'function') return;
|
||||||
|
Object.defineProperty(globalThis, 'structuredClone', {
|
||||||
|
configurable: true,
|
||||||
|
value: <T>(value: T) => deserialize(serialize(value)) as T,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 安装 URL.canParse 兼容实现。 */
|
||||||
|
export function installUrlCanParsePolyfill() {
|
||||||
|
if (typeof urlConstructor.canParse === 'function') return;
|
||||||
|
Object.defineProperty(URL, 'canParse', {
|
||||||
|
configurable: true,
|
||||||
|
value(url: string | URL, base?: string | URL) {
|
||||||
|
try {
|
||||||
|
const parsedUrl =
|
||||||
|
arguments.length > 1
|
||||||
|
? new URL(String(url), String(base))
|
||||||
|
: new URL(String(url));
|
||||||
|
return parsedUrl instanceof URL;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 在业务模块加载前安装 Chrome 90 缺失的运行时能力。 */
|
||||||
|
export function installLegacyBrowserPolyfills() {
|
||||||
|
installObjectHasOwnPolyfill();
|
||||||
|
installArrayAtPolyfill();
|
||||||
|
installStringAtPolyfill();
|
||||||
|
installArrayCopyingPolyfills();
|
||||||
|
installPromiseWithResolversPolyfill();
|
||||||
|
installStructuredClonePolyfill();
|
||||||
|
installUrlCanParsePolyfill();
|
||||||
|
}
|
||||||
|
|
||||||
|
installLegacyBrowserPolyfills();
|
||||||
@@ -78,6 +78,10 @@ function defineApplicationConfig(userConfigPromise?: DefineApplicationOptions) {
|
|||||||
: [],
|
: [],
|
||||||
legalComments: 'none',
|
legalComments: 'none',
|
||||||
},
|
},
|
||||||
|
// 工作区包在本地与构建时均解析到源码,避免 unbuild stub 进入浏览器产物。
|
||||||
|
resolve: {
|
||||||
|
conditions: ['development'],
|
||||||
|
},
|
||||||
plugins,
|
plugins,
|
||||||
server: {
|
server: {
|
||||||
host: true,
|
host: true,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const props = defineProps<{
|
|||||||
<div
|
<div
|
||||||
:class="
|
:class="
|
||||||
cn(
|
cn(
|
||||||
'relative [&>[data-slot=input]]:has-[[data-slot=decrement]]:pl-5 [&>[data-slot=input]]:has-[[data-slot=increment]]:pr-5',
|
'relative [&>[data-slot=input]]:pl-5 [&>[data-slot=input]]:pr-5',
|
||||||
props.class,
|
props.class,
|
||||||
)
|
)
|
||||||
"
|
"
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
padding: 0.6em 0;
|
padding: 0.6em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.vxe-tools--operate:empty,
|
||||||
.vxe-tools--operate:not(:has(button)) {
|
.vxe-tools--operate:not(:has(button)) {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
11
easyflow-ui-usercenter/pnpm-lock.yaml
generated
11
easyflow-ui-usercenter/pnpm-lock.yaml
generated
@@ -634,6 +634,9 @@ importers:
|
|||||||
'@element-plus/icons-vue':
|
'@element-plus/icons-vue':
|
||||||
specifier: ^2.3.2
|
specifier: ^2.3.2
|
||||||
version: 2.3.2(vue@3.5.24(typescript@5.9.3))
|
version: 2.3.2(vue@3.5.24(typescript@5.9.3))
|
||||||
|
'@ungap/structured-clone':
|
||||||
|
specifier: 1.3.0
|
||||||
|
version: 1.3.0
|
||||||
'@vueuse/core':
|
'@vueuse/core':
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 13.9.0(vue@3.5.24(typescript@5.9.3))
|
version: 13.9.0(vue@3.5.24(typescript@5.9.3))
|
||||||
@@ -674,6 +677,9 @@ importers:
|
|||||||
'@types/node-forge':
|
'@types/node-forge':
|
||||||
specifier: ^1.3.14
|
specifier: ^1.3.14
|
||||||
version: 1.3.14
|
version: 1.3.14
|
||||||
|
'@types/ungap__structured-clone':
|
||||||
|
specifier: 1.2.0
|
||||||
|
version: 1.2.0
|
||||||
unplugin-element-plus:
|
unplugin-element-plus:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 0.10.0
|
version: 0.10.0
|
||||||
@@ -4028,6 +4034,9 @@ packages:
|
|||||||
'@types/trusted-types@2.0.7':
|
'@types/trusted-types@2.0.7':
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||||
|
|
||||||
|
'@types/ungap__structured-clone@1.2.0':
|
||||||
|
resolution: {integrity: sha512-ZoaihZNLeZSxESbk9PUAPZOlSpcKx81I1+4emtULDVmBLkYutTcMlCj2K9VNlf9EWODxdO6gkAqEaLorXwZQVA==}
|
||||||
|
|
||||||
'@types/unist@2.0.11':
|
'@types/unist@2.0.11':
|
||||||
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
|
||||||
|
|
||||||
@@ -13006,6 +13015,8 @@ snapshots:
|
|||||||
|
|
||||||
'@types/trusted-types@2.0.7': {}
|
'@types/trusted-types@2.0.7': {}
|
||||||
|
|
||||||
|
'@types/ungap__structured-clone@1.2.0': {}
|
||||||
|
|
||||||
'@types/unist@2.0.11': {}
|
'@types/unist@2.0.11': {}
|
||||||
|
|
||||||
'@types/unist@3.0.3': {}
|
'@types/unist@3.0.3': {}
|
||||||
|
|||||||
Reference in New Issue
Block a user