@lark-apaas/client-toolkit 1.2.10-alpha.4 → 1.2.10-alpha.41

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.
Files changed (62) hide show
  1. package/lib/apis/hooks/useScrollReveal.d.ts +1 -0
  2. package/lib/apis/hooks/useScrollReveal.js +1 -0
  3. package/lib/apis/utils/scopedStorage.d.ts +1 -0
  4. package/lib/apis/utils/scopedStorage.js +2 -0
  5. package/lib/components/AppContainer/api-proxy/core.js +2 -1
  6. package/lib/components/AppContainer/index.d.ts +5 -1
  7. package/lib/components/AppContainer/index.js +14 -27
  8. package/lib/components/AppContainer/safety.js +5 -2
  9. package/lib/components/AppContainer/utils/childApi.js +1 -0
  10. package/lib/components/AppContainer/utils/getLarkUser.js +4 -2
  11. package/lib/components/AppContainer/utils/observable.js +7 -1
  12. package/lib/components/AppContainer/utils/tea.js +1 -1
  13. package/lib/components/ErrorRender/index.js +5 -11
  14. package/lib/components/User/UserSelect.js +1 -13
  15. package/lib/components/theme/index.d.ts +0 -1
  16. package/lib/components/theme/index.js +0 -1
  17. package/lib/components/theme/util.d.ts +0 -2
  18. package/lib/components/theme/util.js +0 -108
  19. package/lib/components/ui/toast.d.ts +2 -0
  20. package/lib/components/ui/toast.js +53 -0
  21. package/lib/hooks/index.d.ts +1 -0
  22. package/lib/hooks/index.js +1 -0
  23. package/lib/hooks/useLogout.js +2 -17
  24. package/lib/hooks/useScrollReveal.d.ts +61 -0
  25. package/lib/hooks/useScrollReveal.js +37 -0
  26. package/lib/index.js +12 -1
  27. package/lib/integrations/dataloom.js +5 -3
  28. package/lib/integrations/services/DepartmentService.js +3 -2
  29. package/lib/integrations/services/UserProfileService.js +3 -2
  30. package/lib/integrations/services/UserService.js +4 -3
  31. package/lib/integrations/services/types.d.ts +1 -0
  32. package/lib/logger/intercept-global-error.js +16 -14
  33. package/lib/runtime/index.d.ts +1 -0
  34. package/lib/runtime/index.js +1 -0
  35. package/lib/runtime/react-devtools-hook.d.ts +19 -0
  36. package/lib/runtime/react-devtools-hook.js +20 -0
  37. package/lib/theme-layer.css +2 -1
  38. package/lib/utils/apiPath.d.ts +5 -0
  39. package/lib/utils/apiPath.js +5 -0
  40. package/lib/utils/axiosConfig.js +6 -5
  41. package/lib/utils/getAppId.d.ts +2 -4
  42. package/lib/utils/getAppId.js +2 -9
  43. package/lib/utils/getInitialInfo.js +5 -3
  44. package/lib/utils/getUserProfile.js +4 -12
  45. package/lib/utils/hmr-api.d.ts +39 -0
  46. package/lib/utils/hmr-api.js +36 -0
  47. package/lib/utils/module-hot.d.ts +7 -2
  48. package/lib/utils/module-hot.js +6 -51
  49. package/lib/utils/requestManager.js +1 -3
  50. package/lib/utils/scopedStorage.d.ts +5 -0
  51. package/lib/utils/scopedStorage.js +46 -0
  52. package/package.json +5 -5
  53. package/lib/apis/tools/generateImage.d.ts +0 -1
  54. package/lib/apis/tools/generateImage.js +0 -1
  55. package/lib/apis/tools/generateTextStream.d.ts +0 -1
  56. package/lib/apis/tools/generateTextStream.js +0 -1
  57. package/lib/components/theme/ui-config.d.ts +0 -1
  58. package/lib/components/theme/ui-config.js +0 -2
  59. package/lib/integrations/generateImage.d.ts +0 -1
  60. package/lib/integrations/generateImage.js +0 -47
  61. package/lib/integrations/generateTextStream.d.ts +0 -21
  62. package/lib/integrations/generateTextStream.js +0 -98
@@ -0,0 +1 @@
1
+ export * from '../../hooks/useScrollReveal';
@@ -0,0 +1 @@
1
+ export * from "../../hooks/useScrollReveal.js";
@@ -0,0 +1 @@
1
+ export { scopedStorage } from '../../utils/scopedStorage';
@@ -0,0 +1,2 @@
1
+ import { scopedStorage } from "../../utils/scopedStorage.js";
2
+ export { scopedStorage };
@@ -139,7 +139,8 @@ class ApiProxy {
139
139
  ...config,
140
140
  headers: {
141
141
  ...this.defaultConfig.headers,
142
- ...config.headers
142
+ ...config.headers,
143
+ 'X-Page-Route': 'undefined' != typeof window ? window.location?.pathname || '/' : '/'
143
144
  }
144
145
  };
145
146
  const requestKey = this.generateRequestKey(mergedConfig);
@@ -4,7 +4,11 @@ import '../../runtime';
4
4
  interface IBaseAuthProviderProps {
5
5
  enableAuth?: boolean;
6
6
  }
7
+ interface IToasterProps {
8
+ /** 禁用内建 Toaster 组件,默认 false */
9
+ disableToaster?: boolean;
10
+ }
7
11
  declare const AppContainer: React.FC<{
8
12
  children: React.ReactNode;
9
- } & IBaseThemeProviderProps & IBaseAuthProviderProps>;
13
+ } & IBaseThemeProviderProps & IBaseAuthProviderProps & IToasterProps>;
10
14
  export default AppContainer;
@@ -4,15 +4,14 @@ import { ConfigProvider } from "antd";
4
4
  import { MiaodaInspector } from "@lark-apaas/miaoda-inspector";
5
5
  import zh_CN from "antd/locale/zh_CN";
6
6
  import IframeBridge from "./IframeBridge.js";
7
- import { defaultUIConfig } from "../theme/ui-config.js";
8
7
  import { Toaster } from "./sonner.js";
9
8
  import { PageHoc } from "./PageHoc.js";
10
- import { findValueByPixel, generateTailwindRadiusToken, themeColorTokenMap, themeMetaOptions } from "../theme/index.js";
11
9
  import { reportTeaEvent } from "./utils/tea.js";
12
10
  import { useAppInfo } from "../../hooks/index.js";
13
11
  import { TrackKey } from "../../types/tea.js";
14
12
  import safety from "./safety.js";
15
13
  import { getAppId } from "../../utils/getAppId.js";
14
+ import { isNewPathEnabled } from "../../utils/apiPath.js";
16
15
  import QueryProvider from "../QueryProvider/index.js";
17
16
  import { AuthProvider } from "@lark-apaas/auth-sdk";
18
17
  import "../../runtime/index.js";
@@ -26,20 +25,10 @@ const readCssVarColor = (varName, fallback)=>{
26
25
  return fallback;
27
26
  }
28
27
  };
28
+ const appFlags = process.env.APP_FLAGS || {};
29
29
  const App = (props)=>{
30
- const { themeMeta = {}, enableAuth } = props;
30
+ const { enableAuth, disableToaster = false } = props;
31
31
  useAppInfo();
32
- const { rem } = findValueByPixel(themeMetaOptions.themeRadius, themeMeta.borderRadius) || {
33
- rem: '0.625'
34
- };
35
- const radiusToken = generateTailwindRadiusToken(Number(rem));
36
- const themeToken = {
37
- ...defaultUIConfig,
38
- common: {
39
- ...defaultUIConfig.common,
40
- borderRadius: radiusToken
41
- }
42
- };
43
32
  useEffect(()=>{
44
33
  if (isMiaodaPreview) fetch(`${location.origin}/ai/api/feida_preview/csrf`).then(()=>{
45
34
  setTimeout(()=>{
@@ -52,26 +41,24 @@ const App = (props)=>{
52
41
  if ('production' === process.env.NODE_ENV) reportTeaEvent({
53
42
  trackKey: TrackKey.VIEW,
54
43
  trackParams: {
55
- artifact_uid: getAppId(window.location.pathname),
44
+ artifact_uid: getAppId(),
56
45
  agent_id: 'agent_miaoda',
57
46
  url: window.location.href
58
47
  }
59
48
  });
60
49
  }, []);
50
+ const appId = getAppId();
51
+ const permissionApiUrl = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/permissions/roles` : `/spark/app/${appId}/runtime/api/v1/permissions/roles`;
61
52
  return /*#__PURE__*/ jsxs(AuthProvider, {
62
53
  config: {
63
- enable: enableAuth
54
+ enable: enableAuth,
55
+ permissionApi: {
56
+ url: permissionApiUrl
57
+ }
64
58
  },
65
59
  children: [
66
- /*#__PURE__*/ jsx(Toaster, {}),
67
- 'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(MiaodaInspector, {
68
- theme: {
69
- themeMetaOptions: themeMetaOptions,
70
- themeColorTokenMap: themeColorTokenMap,
71
- themeToken
72
- },
73
- cwd: process.env.CWD || ''
74
- }),
60
+ !disableToaster && true !== appFlags.customToaster && /*#__PURE__*/ jsx(Toaster, {}),
61
+ 'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(MiaodaInspector, {}),
75
62
  'production' !== process.env.NODE_ENV && /*#__PURE__*/ jsx(IframeBridge, {}),
76
63
  /*#__PURE__*/ jsx(PageHoc, {
77
64
  children: props.children
@@ -80,7 +67,7 @@ const App = (props)=>{
80
67
  });
81
68
  };
82
69
  const AppContainer_AppContainer = (props)=>{
83
- const { children, ...rest } = props;
70
+ const { children } = props;
84
71
  const [cssColors, setCssColors] = useState(()=>({
85
72
  background: readCssVarColor('--background'),
86
73
  destructive: readCssVarColor('--destructive'),
@@ -208,8 +195,8 @@ const AppContainer_AppContainer = (props)=>{
208
195
  }
209
196
  },
210
197
  children: /*#__PURE__*/ jsx(App, {
211
- themeMeta: props.themeMeta,
212
198
  enableAuth: props.enableAuth,
199
+ disableToaster: props.disableToaster,
213
200
  children: children
214
201
  })
215
202
  })
@@ -3,11 +3,12 @@ import { useEffect, useRef, useState } from "react";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover.js";
4
4
  import { getCsrfToken } from "../../utils/getCsrfToken.js";
5
5
  import { getAppId } from "../../utils/getAppId.js";
6
+ import { isNewPathEnabled } from "../../utils/apiPath.js";
6
7
  import { useIsMobile } from "../../hooks/index.js";
7
8
  import { X } from "lucide-react";
8
9
  import { Sheet, SheetContent, SheetTrigger } from "../ui/drawer.js";
9
10
  const Component = ()=>{
10
- const HasClosedKey = `miaoda-creatByMiaoda-has-closed-${getAppId(window.location.pathname)}`;
11
+ const HasClosedKey = `miaoda-creatByMiaoda-has-closed-${getAppId()}`;
11
12
  const [visible, setVisible] = useState(!window.localStorage?.getItem(HasClosedKey));
12
13
  const [open, setOpen] = useState(false);
13
14
  const isMobile = useIsMobile();
@@ -15,7 +16,9 @@ const Component = ()=>{
15
16
  const [isInternetVisible, setIsInternetVisible] = useState(false);
16
17
  const timeoutRef = useRef(null);
17
18
  useEffect(()=>{
18
- fetch(`/spark/b/${getAppId(window.location.pathname)}/tenant_info`, {
19
+ const appId = getAppId();
20
+ const tenantInfoUrl = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/studio/tenant_info` : `/spark/b/${appId}/tenant_info`;
21
+ fetch(tenantInfoUrl, {
19
22
  headers: {
20
23
  'X-Suda-Csrf-Token': getCsrfToken()
21
24
  }
@@ -16,6 +16,7 @@ async function getRoutes() {
16
16
  return routes;
17
17
  }
18
18
  async function getSourceMap() {
19
+ if ('vite' === process.env.BUILD_TOOL) return '';
19
20
  let sourceMapContent = '';
20
21
  try {
21
22
  const basePath = normalizeBasePath(process.env.CLIENT_BASE_PATH);
@@ -1,13 +1,15 @@
1
1
  import { getAppId } from "../../../utils/getAppId.js";
2
2
  import { getCsrfToken } from "../../../utils/getCsrfToken.js";
3
+ import { isNewPathEnabled } from "../../../utils/apiPath.js";
3
4
  async function getLarkUserInfo() {
4
- const appId = getAppId(window.location.pathname);
5
+ const appId = getAppId();
5
6
  if (!appId) return {
6
7
  code: 1,
7
8
  msg: 'appId is required',
8
9
  data: {}
9
10
  };
10
- const response = await fetch(`/spark/b/${appId}/lark/user_info`, {
11
+ const url = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/studio/lark/user_info` : `/spark/b/${appId}/lark/user_info`;
12
+ const response = await fetch(url, {
11
13
  headers: {
12
14
  'X-Suda-Csrf-Token': getCsrfToken()
13
15
  }
@@ -1,11 +1,17 @@
1
1
  import { AppEnv, observable } from "@lark-apaas/observable-web";
2
+ import { isNewPathEnabled } from "../../../utils/apiPath.js";
2
3
  const initObservable = ()=>{
3
4
  try {
4
5
  const appId = window.appId;
5
6
  observable.start({
6
7
  serviceName: "app",
7
8
  env: 'development' === process.env.NODE_ENV ? AppEnv.Dev : AppEnv.Prod,
8
- collectorUrl: {
9
+ collectorUrl: isNewPathEnabled() ? {
10
+ log: `/app/${appId}/__runtime__/api/v1/observability/logs/collect`,
11
+ trace: `/app/${appId}/__runtime__/api/v1/observability/traces/collect`,
12
+ metric: `/app/${appId}/__runtime__/api/v1/observability/metrics/collect`,
13
+ time: `/app/${appId}/__runtime__/api/v1/observability/current_server_timestamp`
14
+ } : {
9
15
  log: `/spark/app/${appId}/runtime/api/v1/observability/logs/collect`,
10
16
  trace: `/spark/app/${appId}/runtime/api/v1/observability/traces/collect`,
11
17
  metric: `/spark/app/${appId}/runtime/api/v1/observability/metrics/collect`,
@@ -16,7 +16,7 @@ async function createTracker() {
16
16
  const userIDEncrypt = userID && '0' !== userID ? encryptTea(userID) : void 0;
17
17
  const tenantIDEncrypt = tenantID && '0' !== tenantID ? encryptTea(tenantID) : void 0;
18
18
  window.collectEvent('init', {
19
- app_id: 672575,
19
+ app_id: 788827,
20
20
  channel: 'cn',
21
21
  disable_auto_pv: false,
22
22
  enable_ab_test: false,
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useEffect } from "react";
3
3
  import { logger } from "../../logger/index.js";
4
- import { createApplyHandle, getModuleHot } from "../../utils/module-hot.js";
4
+ import { getHmrApi } from "../../utils/hmr-api.js";
5
5
  import { submitPostMessage } from "../../utils/postMessage.js";
6
6
  const RenderError = (props)=>{
7
7
  const { error, resetErrorBoundary } = props;
@@ -27,16 +27,10 @@ const RenderError = (props)=>{
27
27
  ]);
28
28
  useEffect(()=>{
29
29
  if (!resetErrorBoundary) return;
30
- const hot = getModuleHot();
31
- if (hot) {
32
- const handler = createApplyHandle((success)=>{
33
- if (success) resetErrorBoundary();
34
- });
35
- hot.addStatusHandler(handler);
36
- return ()=>{
37
- hot.removeStatusHandler(handler);
38
- };
39
- }
30
+ const hmr = getHmrApi();
31
+ if (hmr) return hmr.onSuccess(()=>{
32
+ resetErrorBoundary();
33
+ });
40
34
  }, [
41
35
  resetErrorBoundary
42
36
  ]);
@@ -67,20 +67,8 @@ const UserSelect = ({ mode = 'single', defaultValue, value, onChange, placeholde
67
67
  if (!normalizedIds.length) return void setUiValue(void 0);
68
68
  const fetchProfiles = async ()=>{
69
69
  try {
70
- const ids = normalizedIds.map((id)=>Number(id)).filter((id)=>Number.isFinite(id));
71
- if (!ids.length) {
72
- const profiles = normalizedIds.map((id)=>inputProfilesMap.get(id) ?? {
73
- user_id: id,
74
- name: '',
75
- avatar: '',
76
- email: '',
77
- status: 1
78
- });
79
- setUiValue('single' === mode ? profiles[0] : profiles);
80
- return;
81
- }
82
70
  const dataloom = await getDataloom();
83
- const response = await dataloom.service.user.getByIds(ids);
71
+ const response = await dataloom.service.user.getByIds(normalizedIds);
84
72
  const fetchedList = Array.isArray(response?.data) ? response?.data : Array.isArray(response?.data?.user_list) ? response?.data?.user_list : [];
85
73
  const fetchedMap = new Map();
86
74
  fetchedList.forEach((profile)=>{
@@ -1,3 +1,2 @@
1
1
  export * from './constants';
2
2
  export * from './util';
3
- export * from './ui-config';
@@ -1,3 +1,2 @@
1
1
  export * from "./constants.js";
2
2
  export * from "./util.js";
3
- export * from "./ui-config.js";
@@ -1,5 +1,4 @@
1
1
  import { ITheme } from "../../types";
2
- import { UIComponentConfig } from "./ui-config";
3
2
  export interface IBaseThemeProviderProps {
4
3
  defaultTheme?: ITheme;
5
4
  themeMeta?: Partial<IThemeTokenMeta>;
@@ -18,4 +17,3 @@ export interface IThemeTokenMeta {
18
17
  */
19
18
  spacing: number;
20
19
  }
21
- export declare const generateTailwindRadiusToken: (radiusRemValue: number) => UIComponentConfig;
@@ -1,108 +0,0 @@
1
- const generateTailwindRadiusToken = (radiusRemValue)=>{
2
- try {
3
- const rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize);
4
- const radiusPx = radiusRemValue * rootFontSize;
5
- const tempElement = document.createElement('div');
6
- tempElement.style.position = 'absolute';
7
- tempElement.style.visibility = 'hidden';
8
- document.body.appendChild(tempElement);
9
- tempElement.style.borderRadius = `${radiusPx}px`;
10
- const computed = getComputedStyle(tempElement).borderRadius;
11
- document.body.removeChild(tempElement);
12
- if (computed && 'auto' !== computed && 'initial' !== computed) return {
13
- type: 'select',
14
- options: [
15
- {
16
- value: 'rounded-none',
17
- label: 'none(0px)',
18
- rawValue: '0'
19
- },
20
- {
21
- value: 'rounded-sm',
22
- label: `sm (${Math.max(0, radiusPx - 4)}px)`,
23
- rawValue: `${Math.max(0, radiusPx - 4)}`
24
- },
25
- {
26
- value: 'rounded',
27
- label: 's (4px)',
28
- rawValue: '4'
29
- },
30
- {
31
- value: 'rounded-md',
32
- label: `m (${Math.max(0, radiusPx - 2)}px)`,
33
- rawValue: `${Math.max(0, radiusPx - 2)}`
34
- },
35
- {
36
- value: 'rounded-lg',
37
- label: `l (${radiusPx}px)`,
38
- rawValue: `${radiusPx}`
39
- },
40
- {
41
- value: 'rounded-xl',
42
- label: `xl (${radiusPx + 4}px)`,
43
- rawValue: `${radiusPx + 4}`
44
- },
45
- {
46
- value: 'rounded-2xl',
47
- label: '2xl (16px)',
48
- rawValue: '16'
49
- },
50
- {
51
- value: 'rounded-full',
52
- label: 'Full',
53
- rawValue: '9999'
54
- }
55
- ]
56
- };
57
- throw new Error('Browser calculation validation failed');
58
- } catch (error) {
59
- console.warn('Failed to get computed values from browser, falling back to default calculation:', error);
60
- const radiusPx = 16 * radiusRemValue;
61
- return {
62
- type: 'select',
63
- options: [
64
- {
65
- value: 'rounded-none',
66
- label: 'none(0px)',
67
- rawValue: '0'
68
- },
69
- {
70
- value: 'rounded-sm',
71
- label: `sm (${Math.max(0, radiusPx - 4)}px)`,
72
- rawValue: `${Math.max(0, radiusPx - 4)}`
73
- },
74
- {
75
- value: 'rounded',
76
- label: 's (4px)',
77
- rawValue: '4'
78
- },
79
- {
80
- value: 'rounded-md',
81
- label: `m (${Math.max(0, radiusPx - 2)}px)`,
82
- rawValue: `${Math.max(0, radiusPx - 2)}`
83
- },
84
- {
85
- value: 'rounded-lg',
86
- label: `l (${radiusPx}px)`,
87
- rawValue: `${radiusPx}`
88
- },
89
- {
90
- value: 'rounded-xl',
91
- label: `xl (${radiusPx + 4}px)`,
92
- rawValue: `${radiusPx + 4}`
93
- },
94
- {
95
- value: 'rounded-2xl',
96
- label: '2xl (16px)',
97
- rawValue: '16'
98
- },
99
- {
100
- value: 'rounded-full',
101
- label: 'Full',
102
- rawValue: '9999'
103
- }
104
- ]
105
- };
106
- }
107
- };
108
- export { generateTailwindRadiusToken };
@@ -0,0 +1,2 @@
1
+ /** 显示 toast 提示(兼容 PC/移动端) */
2
+ export declare function showToast(message: string, duration?: number): Promise<void>;
@@ -0,0 +1,53 @@
1
+ function showToast(message, duration = 3000) {
2
+ return new Promise((resolve)=>{
3
+ const toast = document.createElement('div');
4
+ const iconSvg = `<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
5
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M8 1.333a6.667 6.667 0 1 1 0 13.334A6.667 6.667 0 0 1 8 1.333ZM8 10a.667.667 0 1 0 0 1.333A.667.667 0 0 0 8 10Zm0-5.333a.667.667 0 0 0-.667.666v3.334a.667.667 0 0 0 1.334 0V5.333A.667.667 0 0 0 8 4.667Z" fill="#ff811a"/>
6
+ </svg>`;
7
+ const iconWrapper = document.createElement('span');
8
+ iconWrapper.innerHTML = iconSvg;
9
+ Object.assign(iconWrapper.style, {
10
+ display: 'flex',
11
+ alignItems: 'center',
12
+ marginRight: '8px',
13
+ flexShrink: '0'
14
+ });
15
+ const textWrapper = document.createElement('span');
16
+ textWrapper.textContent = message;
17
+ toast.appendChild(iconWrapper);
18
+ toast.appendChild(textWrapper);
19
+ Object.assign(toast.style, {
20
+ position: 'fixed',
21
+ top: '32px',
22
+ left: '50%',
23
+ transform: 'translate(-50%, -50%)',
24
+ display: 'flex',
25
+ alignItems: 'center',
26
+ padding: '12px 16px',
27
+ backgroundColor: '#fff',
28
+ color: '#1f2329',
29
+ border: '1px solid #dee0e3',
30
+ borderRadius: '6px',
31
+ fontSize: '14px',
32
+ lineHeight: '1.5',
33
+ zIndex: '99999',
34
+ maxWidth: '80vw',
35
+ wordBreak: 'break-word',
36
+ boxShadow: '0 4px 8px rgba(0, 0, 0, 0.03), 0 3px 6px rgba(0, 0, 0, 0.05), 0 6px 18px rgba(0, 0, 0, 0.03)',
37
+ opacity: '0',
38
+ transition: 'opacity 0.3s ease'
39
+ });
40
+ document.body.appendChild(toast);
41
+ requestAnimationFrame(()=>{
42
+ toast.style.opacity = '1';
43
+ });
44
+ setTimeout(()=>{
45
+ toast.style.opacity = '0';
46
+ setTimeout(()=>{
47
+ document.body.removeChild(toast);
48
+ resolve();
49
+ }, 300);
50
+ }, duration);
51
+ });
52
+ }
53
+ export { showToast };
@@ -2,3 +2,4 @@ export * from './useAppInfo';
2
2
  export * from './useCurrentUserProfile';
3
3
  export * from './useIsMobile';
4
4
  export * from './useLogout';
5
+ export * from './useScrollReveal';
@@ -2,3 +2,4 @@ export * from "./useAppInfo.js";
2
2
  export * from "./useCurrentUserProfile.js";
3
3
  export * from "./useIsMobile.js";
4
4
  export * from "./useLogout.js";
5
+ export * from "./useScrollReveal.js";
@@ -1,28 +1,13 @@
1
1
  import { useState } from "react";
2
2
  import { getDataloom } from "../integrations/dataloom.js";
3
- import { isSparkRuntime } from "../utils/utils.js";
4
3
  function useLogout() {
5
4
  const [isLoading, setIsLoading] = useState(false);
6
5
  async function handlerLogout() {
7
6
  if ('production' !== process.env.NODE_ENV) return void console.log('只有生产环境才执行登出');
8
- if (isSparkRuntime()) {
9
- setIsLoading(true);
10
- try {
11
- const dataloom = await getDataloom();
12
- await dataloom.service.session.signOut();
13
- } catch (error) {
14
- console.error('登出失败', error);
15
- } finally{
16
- setIsLoading(false);
17
- }
18
- return;
19
- }
20
7
  setIsLoading(true);
21
8
  try {
22
- await fetch('/ai/api/ui/page/logout', {
23
- method: 'GET'
24
- });
25
- window.location.reload();
9
+ const dataloom = await getDataloom();
10
+ await dataloom.service.session.signOut();
26
11
  } catch (error) {
27
12
  console.error('登出失败', error);
28
13
  } finally{
@@ -0,0 +1,61 @@
1
+ import { RefObject } from 'react';
2
+ /**
3
+ * Options for useScrollReveal hook
4
+ */
5
+ export interface UseScrollRevealOptions<T extends HTMLElement = HTMLElement> {
6
+ /** Ref to the container element to scope the query (default: document.body) */
7
+ containerRef?: RefObject<T | null>;
8
+ /** IntersectionObserver threshold - element visibility percentage to trigger (default: 0.1) */
9
+ threshold?: number;
10
+ /** IntersectionObserver rootMargin - margin around root (default: '0px 0px -50px 0px') */
11
+ rootMargin?: string;
12
+ /** CSS class to add when element becomes visible (default: 'visible') */
13
+ visibleClass?: string;
14
+ /** CSS selector for elements to observe (default: '.scroll-reveal') */
15
+ selector?: string;
16
+ }
17
+ /**
18
+ * Hook to enable scroll reveal animations using IntersectionObserver.
19
+ *
20
+ * Observes elements with the specified selector and adds a visibility class
21
+ * when they enter the viewport.
22
+ *
23
+ * @param options - Configuration options
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * // Usage without parameters (queries entire document.body)
28
+ * import { useScrollReveal } from '@lark-apaas/client-toolkit/hooks/useScrollReveal';
29
+ *
30
+ * function MyComponent() {
31
+ * useScrollReveal();
32
+ *
33
+ * return <div className="scroll-reveal">I will fade in on scroll!</div>;
34
+ * }
35
+ * ```
36
+ *
37
+ * @example
38
+ * ```tsx
39
+ * // Usage with container ref
40
+ * import { useRef } from 'react';
41
+ * import { useScrollReveal } from '@lark-apaas/client-toolkit/hooks/useScrollReveal';
42
+ *
43
+ * function MyComponent() {
44
+ * const containerRef = useRef<HTMLDivElement>(null);
45
+ * useScrollReveal({ containerRef });
46
+ *
47
+ * return (
48
+ * <div ref={containerRef}>
49
+ * <div className="scroll-reveal">I will fade in on scroll!</div>
50
+ * </div>
51
+ * );
52
+ * }
53
+ * ```
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * // Usage with custom options
58
+ * useScrollReveal({ threshold: 0.2, visibleClass: 'animate-in' });
59
+ * ```
60
+ */
61
+ export declare function useScrollReveal<T extends HTMLElement = HTMLElement>(options?: UseScrollRevealOptions<T>): void;
@@ -0,0 +1,37 @@
1
+ import { useEffect } from "react";
2
+ const DEFAULT_OPTIONS = {
3
+ threshold: 0.1,
4
+ rootMargin: '0px 0px -50px 0px',
5
+ visibleClass: 'visible',
6
+ selector: '.scroll-reveal'
7
+ };
8
+ function useScrollReveal(options) {
9
+ const { containerRef, threshold, rootMargin, visibleClass, selector } = {
10
+ ...DEFAULT_OPTIONS,
11
+ ...options
12
+ };
13
+ useEffect(()=>{
14
+ const container = containerRef?.current ?? document.body;
15
+ if (!container) return;
16
+ const observer = new IntersectionObserver((entries)=>{
17
+ entries.forEach((entry)=>{
18
+ if (entry.isIntersecting) entry.target.classList.add(visibleClass);
19
+ });
20
+ }, {
21
+ threshold,
22
+ rootMargin
23
+ });
24
+ const elements = container.querySelectorAll(selector);
25
+ elements.forEach((el)=>observer.observe(el));
26
+ return ()=>{
27
+ observer.disconnect();
28
+ };
29
+ }, [
30
+ containerRef,
31
+ threshold,
32
+ rootMargin,
33
+ visibleClass,
34
+ selector
35
+ ]);
36
+ }
37
+ export { useScrollReveal };
package/lib/index.js CHANGED
@@ -1,15 +1,26 @@
1
1
  import { createClient } from "@lark-apaas/client-capability";
2
2
  import { normalizeBasePath } from "./utils/utils.js";
3
+ import { getAppId } from "./utils/getAppId.js";
4
+ import { isNewPathEnabled } from "./utils/apiPath.js";
3
5
  import { logger } from "./logger/index.js";
6
+ import { showToast } from "./components/ui/toast.js";
4
7
  import { version } from "../package.json";
8
+ const _appId = getAppId();
9
+ const _acquireUploadUrl = isNewPathEnabled() ? `/app/${_appId}/__runtime__/api/v1/studio/plugins/tmp_files/acquire_upload_url` : "/af/api/v1/studio/plugins/tmp_files/acquire_upload_url";
10
+ const _acquireDownloadUrl = isNewPathEnabled() ? `/app/${_appId}/__runtime__/api/v1/studio/plugins/tmp_files/acquire_download_url` : "/af/api/v1/studio/plugins/tmp_files/acquire_download_url";
5
11
  const capabilityClient = createClient({
6
12
  baseURL: normalizeBasePath(process.env.CLIENT_BASE_PATH),
13
+ acquireUploadUrl: _acquireUploadUrl,
14
+ acquireDownloadUrl: _acquireDownloadUrl,
7
15
  fetchOptions: {
8
16
  headers: {
9
17
  'X-Suda-Csrf-Token': window.csrfToken ?? ''
10
18
  }
11
19
  },
12
- logger: logger
20
+ logger: logger,
21
+ onRateLimitError: ()=>{
22
+ showToast('应用额度已耗尽,请联系应用开发者');
23
+ }
13
24
  });
14
25
  const src = {
15
26
  version: version
@@ -3,9 +3,11 @@ import { createClient } from "@data-loom/js";
3
3
  import { getAppId } from "../utils/getAppId.js";
4
4
  import { getInitialInfo } from "../utils/getInitialInfo.js";
5
5
  const createDataLoomClient = (url, pat)=>{
6
- if (!url) return null;
7
- const { baseUrl, workspace } = splitWorkspaceUrl(url);
8
- const appId = getAppId(window.location.pathname);
6
+ const { baseUrl, workspace } = url ? splitWorkspaceUrl(url) : {
7
+ baseUrl: '',
8
+ workspace: ''
9
+ };
10
+ const appId = getAppId();
9
11
  return createClient(baseUrl, pat, workspace, {
10
12
  global: {
11
13
  enableDataloomLog: 'production' !== process.env.NODE_ENV,