openxiangda 1.0.62 → 1.0.64

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 (28) hide show
  1. package/package.json +1 -1
  2. package/packages/sdk/dist/runtime/index.cjs +276 -13
  3. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  4. package/packages/sdk/dist/runtime/index.d.mts +1 -1
  5. package/packages/sdk/dist/runtime/index.d.ts +1 -1
  6. package/packages/sdk/dist/runtime/index.mjs +276 -13
  7. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  8. package/packages/sdk/dist/runtime/react.cjs +277 -13
  9. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  10. package/packages/sdk/dist/runtime/react.d.mts +60 -7
  11. package/packages/sdk/dist/runtime/react.d.ts +60 -7
  12. package/packages/sdk/dist/runtime/react.mjs +277 -13
  13. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  14. package/templates/openxiangda-react-spa/src/app/router.tsx +12 -1
  15. package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +384 -96
  16. package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +25 -3
  17. package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +101 -22
  18. package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +203 -41
  19. package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +80 -11
  20. package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +67 -14
  21. package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +153 -30
  22. package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +53 -14
  23. package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +46 -6
  24. package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +26 -11
  25. package/templates/openxiangda-react-spa/src/shared/mac-admin.tsx +332 -0
  26. package/templates/openxiangda-react-spa/src/styles/index.css +42 -4
  27. package/templates/openxiangda-react-spa/tailwind.config.cjs +8 -0
  28. package/templates/openxiangda-react-spa/vite.config.ts +31 -0
@@ -0,0 +1,332 @@
1
+ import type { ComponentType, ReactNode } from "react";
2
+
3
+ export type MacTone =
4
+ | "blue"
5
+ | "emerald"
6
+ | "amber"
7
+ | "rose"
8
+ | "violet"
9
+ | "slate";
10
+
11
+ export interface MacAction {
12
+ label: string;
13
+ onClick?: () => void;
14
+ href?: string;
15
+ }
16
+
17
+ const toneClasses: Record<MacTone, { soft: string; text: string; dot: string; ring: string }> = {
18
+ amber: {
19
+ dot: "bg-amber-500",
20
+ ring: "ring-amber-200",
21
+ soft: "bg-amber-50 text-amber-700",
22
+ text: "text-amber-700",
23
+ },
24
+ blue: {
25
+ dot: "bg-blue-500",
26
+ ring: "ring-blue-200",
27
+ soft: "bg-blue-50 text-blue-700",
28
+ text: "text-blue-700",
29
+ },
30
+ emerald: {
31
+ dot: "bg-emerald-500",
32
+ ring: "ring-emerald-200",
33
+ soft: "bg-emerald-50 text-emerald-700",
34
+ text: "text-emerald-700",
35
+ },
36
+ rose: {
37
+ dot: "bg-rose-500",
38
+ ring: "ring-rose-200",
39
+ soft: "bg-rose-50 text-rose-700",
40
+ text: "text-rose-700",
41
+ },
42
+ slate: {
43
+ dot: "bg-slate-500",
44
+ ring: "ring-slate-200",
45
+ soft: "bg-slate-100 text-slate-700",
46
+ text: "text-slate-700",
47
+ },
48
+ violet: {
49
+ dot: "bg-violet-500",
50
+ ring: "ring-violet-200",
51
+ soft: "bg-violet-50 text-violet-700",
52
+ text: "text-violet-700",
53
+ },
54
+ };
55
+
56
+ export function cn(...values: Array<string | false | null | undefined>) {
57
+ return values.filter(Boolean).join(" ");
58
+ }
59
+
60
+ export function MacPageHeader({
61
+ actions,
62
+ description,
63
+ eyebrow,
64
+ meta,
65
+ title,
66
+ }: {
67
+ actions?: ReactNode;
68
+ description?: ReactNode;
69
+ eyebrow?: ReactNode;
70
+ meta?: ReactNode;
71
+ title: ReactNode;
72
+ }) {
73
+ return (
74
+ <section className="relative overflow-hidden rounded-2xl border border-white/70 bg-white/[0.85] p-5 shadow-[0_18px_60px_rgba(15,23,42,0.07)] ring-1 ring-slate-900/5 backdrop-blur">
75
+ <div className="absolute inset-x-0 top-0 h-1 bg-[linear-gradient(90deg,#4f46e5,#06b6d4,#22c55e,#f59e0b)]" />
76
+ <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
77
+ <div className="min-w-0">
78
+ {eyebrow ? (
79
+ <div className="mb-2 text-xs font-semibold uppercase tracking-[0.12em] text-slate-500">
80
+ {eyebrow}
81
+ </div>
82
+ ) : null}
83
+ <h1 className="text-2xl font-semibold text-slate-950">{title}</h1>
84
+ {description ? (
85
+ <p className="mt-2 max-w-3xl text-sm leading-6 text-slate-600">{description}</p>
86
+ ) : null}
87
+ {meta ? <div className="mt-4 flex flex-wrap gap-2">{meta}</div> : null}
88
+ </div>
89
+ {actions ? <div className="flex shrink-0 flex-wrap gap-2">{actions}</div> : null}
90
+ </div>
91
+ </section>
92
+ );
93
+ }
94
+
95
+ export function MacPanel({
96
+ children,
97
+ className,
98
+ title,
99
+ description,
100
+ action,
101
+ }: {
102
+ children: ReactNode;
103
+ className?: string;
104
+ title?: ReactNode;
105
+ description?: ReactNode;
106
+ action?: ReactNode;
107
+ }) {
108
+ return (
109
+ <section
110
+ className={cn(
111
+ "rounded-2xl border border-white/70 bg-white/[0.88] p-5 shadow-[0_18px_55px_rgba(15,23,42,0.06)] ring-1 ring-slate-900/5 backdrop-blur",
112
+ className,
113
+ )}
114
+ >
115
+ {title || description || action ? (
116
+ <div className="mb-4 flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
117
+ <div className="min-w-0">
118
+ {title ? <h2 className="text-base font-semibold text-slate-950">{title}</h2> : null}
119
+ {description ? (
120
+ <p className="mt-1 text-sm leading-6 text-slate-500">{description}</p>
121
+ ) : null}
122
+ </div>
123
+ {action ? <div className="shrink-0">{action}</div> : null}
124
+ </div>
125
+ ) : null}
126
+ {children}
127
+ </section>
128
+ );
129
+ }
130
+
131
+ export function MacMetricCard({
132
+ caption,
133
+ icon: Icon,
134
+ label,
135
+ tone = "blue",
136
+ value,
137
+ }: {
138
+ caption?: ReactNode;
139
+ icon?: ComponentType<{ size?: string | number; className?: string }>;
140
+ label: ReactNode;
141
+ tone?: MacTone;
142
+ value: ReactNode;
143
+ }) {
144
+ const classes = toneClasses[tone];
145
+ return (
146
+ <div className="group rounded-2xl border border-white/70 bg-white/[0.82] p-4 shadow-[0_16px_45px_rgba(15,23,42,0.05)] ring-1 ring-slate-900/5 transition hover:-translate-y-0.5 hover:shadow-[0_22px_60px_rgba(15,23,42,0.09)]">
147
+ <div className="flex items-start justify-between gap-3">
148
+ <div className={cn("grid h-10 w-10 place-items-center rounded-xl", classes.soft)}>
149
+ {Icon ? <Icon size={20} /> : <span className={cn("h-2.5 w-2.5 rounded-full", classes.dot)} />}
150
+ </div>
151
+ <span className={cn("h-2 w-2 rounded-full ring-4", classes.dot, classes.ring)} />
152
+ </div>
153
+ <div className="mt-4 text-sm text-slate-500">{label}</div>
154
+ <div className="mt-1 text-2xl font-semibold text-slate-950">{value}</div>
155
+ {caption ? <div className="mt-3 text-xs leading-5 text-slate-500">{caption}</div> : null}
156
+ </div>
157
+ );
158
+ }
159
+
160
+ export function MacStatusPill({
161
+ children,
162
+ tone = "slate",
163
+ }: {
164
+ children: ReactNode;
165
+ tone?: MacTone;
166
+ }) {
167
+ const classes = toneClasses[tone];
168
+ return (
169
+ <span className={cn("inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-medium", classes.soft)}>
170
+ <span className={cn("h-1.5 w-1.5 rounded-full", classes.dot)} />
171
+ {children}
172
+ </span>
173
+ );
174
+ }
175
+
176
+ export function MacStatePage({
177
+ actions,
178
+ description,
179
+ fullScreen = false,
180
+ icon,
181
+ status,
182
+ title,
183
+ }: {
184
+ actions?: ReactNode;
185
+ description?: ReactNode;
186
+ fullScreen?: boolean;
187
+ icon?: ReactNode;
188
+ status?: ReactNode;
189
+ title: ReactNode;
190
+ }) {
191
+ return (
192
+ <main
193
+ className={cn(
194
+ "grid place-items-center bg-slate-100/80 px-5",
195
+ fullScreen ? "min-h-screen" : "min-h-[460px]",
196
+ )}
197
+ >
198
+ <section className="w-full max-w-lg overflow-hidden rounded-3xl border border-white/70 bg-white/90 p-7 text-center shadow-[0_24px_80px_rgba(15,23,42,0.10)] ring-1 ring-slate-900/5 backdrop-blur">
199
+ {status ? <div className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-400">{status}</div> : null}
200
+ <div className="mx-auto mt-4 grid h-14 w-14 place-items-center rounded-2xl bg-slate-950 text-white shadow-lg shadow-slate-300/60">
201
+ {icon || "OX"}
202
+ </div>
203
+ <h1 className="mt-5 text-2xl font-semibold text-slate-950">{title}</h1>
204
+ {description ? (
205
+ <p className="mx-auto mt-3 max-w-sm text-sm leading-6 text-slate-600">{description}</p>
206
+ ) : null}
207
+ {actions ? <div className="mt-6 flex flex-wrap items-center justify-center gap-3">{actions}</div> : null}
208
+ </section>
209
+ </main>
210
+ );
211
+ }
212
+
213
+ export function MacPrimaryButton({
214
+ children,
215
+ className,
216
+ onClick,
217
+ type = "button",
218
+ }: {
219
+ children: ReactNode;
220
+ className?: string;
221
+ onClick?: () => void;
222
+ type?: "button" | "submit";
223
+ }) {
224
+ return (
225
+ <button
226
+ className={cn(
227
+ "inline-flex h-10 items-center justify-center gap-2 rounded-xl bg-slate-950 px-4 text-sm font-semibold text-white shadow-lg shadow-slate-300/60 transition hover:bg-slate-800",
228
+ className,
229
+ )}
230
+ onClick={onClick}
231
+ type={type}
232
+ >
233
+ {children}
234
+ </button>
235
+ );
236
+ }
237
+
238
+ export function MacSecondaryButton({
239
+ children,
240
+ className,
241
+ onClick,
242
+ type = "button",
243
+ }: {
244
+ children: ReactNode;
245
+ className?: string;
246
+ onClick?: () => void;
247
+ type?: "button" | "submit";
248
+ }) {
249
+ return (
250
+ <button
251
+ className={cn(
252
+ "inline-flex h-10 items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-4 text-sm font-semibold text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50",
253
+ className,
254
+ )}
255
+ onClick={onClick}
256
+ type={type}
257
+ >
258
+ {children}
259
+ </button>
260
+ );
261
+ }
262
+
263
+ export function MacListItem({
264
+ children,
265
+ icon,
266
+ tone = "blue",
267
+ }: {
268
+ children: ReactNode;
269
+ icon?: ReactNode;
270
+ tone?: MacTone;
271
+ }) {
272
+ const classes = toneClasses[tone];
273
+ return (
274
+ <div className="flex gap-3 rounded-2xl border border-slate-200/70 bg-white/[0.72] p-3">
275
+ <div className={cn("grid h-9 w-9 shrink-0 place-items-center rounded-xl", classes.soft)}>
276
+ {icon || <span className={cn("h-2 w-2 rounded-full", classes.dot)} />}
277
+ </div>
278
+ <div className="min-w-0 flex-1">{children}</div>
279
+ </div>
280
+ );
281
+ }
282
+
283
+ export function MacDiagnosticPanel({
284
+ data,
285
+ title = "调试信息",
286
+ }: {
287
+ data: unknown;
288
+ title?: string;
289
+ }) {
290
+ return (
291
+ <details className="group rounded-2xl border border-slate-200/80 bg-white/70 p-4">
292
+ <summary className="cursor-pointer select-none text-sm font-semibold text-slate-700">
293
+ {title}
294
+ <span className="ml-2 text-xs font-normal text-slate-400">展开查看 runtime payload</span>
295
+ </summary>
296
+ <pre className="ox-scrollbar mt-4 max-h-96 overflow-auto rounded-2xl bg-slate-950 p-4 text-xs leading-6 text-slate-100">
297
+ {JSON.stringify(data, null, 2)}
298
+ </pre>
299
+ </details>
300
+ );
301
+ }
302
+
303
+ export function MacTrendBars({
304
+ values,
305
+ tone = "blue",
306
+ }: {
307
+ values: number[];
308
+ tone?: MacTone;
309
+ }) {
310
+ const color =
311
+ tone === "emerald"
312
+ ? "bg-emerald-500"
313
+ : tone === "amber"
314
+ ? "bg-amber-500"
315
+ : tone === "rose"
316
+ ? "bg-rose-500"
317
+ : tone === "violet"
318
+ ? "bg-violet-500"
319
+ : "bg-blue-500";
320
+ const max = Math.max(...values, 1);
321
+ return (
322
+ <div className="flex h-32 items-end gap-2 rounded-2xl bg-slate-100/70 p-4">
323
+ {values.map((value, index) => (
324
+ <div
325
+ className={cn("min-w-0 flex-1 rounded-t-lg opacity-80", color)}
326
+ key={`${value}-${index}`}
327
+ style={{ height: `${Math.max(12, (value / max) * 100)}%` }}
328
+ />
329
+ ))}
330
+ </div>
331
+ );
332
+ }
@@ -13,11 +13,49 @@ body {
13
13
  font-family:
14
14
  Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
15
15
  sans-serif;
16
- background: #f6f7f9;
16
+ background: #eef2f7;
17
+ color: #0f172a;
18
+ overflow-x: hidden;
19
+ }
20
+
21
+ button,
22
+ input,
23
+ select,
24
+ textarea {
25
+ font: inherit;
26
+ }
27
+
28
+ button {
29
+ border: 0;
30
+ }
31
+
32
+ a {
33
+ color: inherit;
34
+ text-decoration: none;
17
35
  }
18
36
 
19
37
  .ox-panel {
20
- border: 1px solid rgb(226 232 240);
21
- border-radius: 8px;
22
- background: rgb(255 255 255 / 0.92);
38
+ border: 1px solid rgb(255 255 255 / 0.72);
39
+ border-radius: 16px;
40
+ background: rgb(255 255 255 / 0.88);
41
+ box-shadow: 0 18px 55px rgb(15 23 42 / 0.06);
42
+ }
43
+
44
+ .ox-scrollbar {
45
+ scrollbar-color: rgb(148 163 184 / 0.7) transparent;
46
+ scrollbar-width: thin;
47
+ }
48
+
49
+ .ox-scrollbar::-webkit-scrollbar {
50
+ height: 10px;
51
+ width: 10px;
52
+ }
53
+
54
+ .ox-scrollbar::-webkit-scrollbar-thumb {
55
+ background: rgb(148 163 184 / 0.65);
56
+ border-radius: 999px;
57
+ }
58
+
59
+ .ox-scrollbar::-webkit-scrollbar-track {
60
+ background: transparent;
23
61
  }
@@ -23,7 +23,15 @@ module.exports = {
23
23
  colors: {
24
24
  graphite: "#1f2937",
25
25
  },
26
+ borderRadius: {
27
+ sm: "0.125rem",
28
+ md: "0.375rem",
29
+ lg: "0.5rem",
30
+ },
26
31
  },
27
32
  },
33
+ corePlugins: {
34
+ preflight: true,
35
+ },
28
36
  plugins: [],
29
37
  };
@@ -1,4 +1,5 @@
1
1
  import react from "@vitejs/plugin-react";
2
+ import { existsSync } from "node:fs";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { defineConfig, loadEnv } from "vite";
4
5
 
@@ -17,6 +18,35 @@ const toPort = (value?: string) => {
17
18
  return Number.isFinite(port) && port > 0 ? port : 5174;
18
19
  };
19
20
 
21
+ const localSdkRoot = fileURLToPath(
22
+ new URL("../../packages/sdk/src", import.meta.url),
23
+ );
24
+ const localSdkAliases = existsSync(localSdkRoot)
25
+ ? [
26
+ {
27
+ find: "openxiangda/runtime/react",
28
+ replacement: fileURLToPath(
29
+ new URL(
30
+ "../../packages/sdk/src/runtime/react/openxiangdaProvider.tsx",
31
+ import.meta.url,
32
+ ),
33
+ ),
34
+ },
35
+ {
36
+ find: "openxiangda/runtime",
37
+ replacement: fileURLToPath(
38
+ new URL("../../packages/sdk/src/runtime/index.ts", import.meta.url),
39
+ ),
40
+ },
41
+ {
42
+ find: "openxiangda",
43
+ replacement: fileURLToPath(
44
+ new URL("../../packages/sdk/src/components/index.ts", import.meta.url),
45
+ ),
46
+ },
47
+ ]
48
+ : [];
49
+
20
50
  export default defineConfig(({ mode }) => {
21
51
  const env = loadEnv(mode, process.cwd(), "");
22
52
  const serviceTarget = resolveProxyTarget(
@@ -44,6 +74,7 @@ export default defineConfig(({ mode }) => {
44
74
  plugins: [react()],
45
75
  resolve: {
46
76
  alias: [
77
+ ...localSdkAliases,
47
78
  {
48
79
  find: "@",
49
80
  replacement: fileURLToPath(new URL("./src", import.meta.url)),