update: style

This commit is contained in:
tao.chen
2026-08-28 12:00:14 +08:00
parent e2fd7eba63
commit 9badd3597f
34 changed files with 207 additions and 237 deletions
+18 -61
View File
@@ -3,9 +3,18 @@
@theme {
--font-sans: Inter, "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
/* STYLE_GUIDE §3.2 字号补充 token */
--text-2xs: 0.625rem; /* 10px */
--text-3xs: 0.5625rem; /* 9px */
--text-4xs: 0.5rem; /* 8px */
/* STYLE_GUIDE §3.3 字重补充 token */
--font-weight-medium-plus: 650;
--color-brand: #1978d4;
--color-brand-strong: #0e5fb9;
--color-brand-soft: #edf6ff;
--color-brand-deep: #0e5bad;
--color-success: #18b979;
--color-success-strong: #0f7d55;
@@ -21,6 +30,7 @@
--color-ink: #27394d;
--color-ink-muted: #5d7186;
--color-ink-subtle: #8b99a8;
--color-ink-caption: #748598;
--color-line: #dce4ec;
--color-line-soft: #edf1f5;
@@ -29,6 +39,13 @@
--color-bg-panel: #ffffff;
--color-bg-canvas: #f9fbfd;
--color-bg-log: #17212b;
/* STYLE_GUIDE §4.2 间距补充 token */
--spacing-page-x: 22px;
--spacing-button-x: 15px;
--spacing-gap-icon: 7px;
--spacing-gap-sm: 5px;
--spacing-gap-md: 9px;
}
@theme inline {
--color-sidebar: var(--sidebar-background);
@@ -256,19 +273,13 @@
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
/* Shared UI classes still used by admin / schedules pages */
.icon-button {
display: grid;
width: 34px;
height: 34px;
place-items: center;
border: 1px solid #dfe6ed;
border: 1px solid var(--color-line);
border-radius: 7px;
background: #fff;
cursor: pointer;
@@ -290,57 +301,3 @@
font-size: 14px;
font-weight: 700;
}
.primary-button,
.secondary-button {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 7px;
border-radius: 6px;
cursor: pointer;
font-weight: 650;
}
.primary-button {
min-height: 37px;
padding: 0 15px;
border: 1px solid #126ac3;
color: #fff;
background: linear-gradient(135deg, #1881e7, #1268c9);
box-shadow: 0 6px 15px rgb(20 111 202 / 18%);
font-size: 12px;
}
.primary-button:hover:not(:disabled) {
background: linear-gradient(135deg, #1175d8, #0e5bad);
}
.primary-button:disabled {
cursor: not-allowed;
opacity: 0.58;
}
.secondary-button {
min-height: 37px;
padding: 0 15px;
border: 1px solid #d8e1e9;
color: #56687c;
background: #fff;
font-size: 12px;
}
.button-spinner {
width: 13px;
height: 13px;
border: 2px solid rgb(255 255 255 / 45%);
border-top-color: #fff;
border-radius: 50%;
animation: spin 0.7s linear infinite;
}
.button-spinner.button-spinner--blue,
.button-spinner--blue {
border-color: rgb(34 121 199 / 22%);
border-top-color: #2279c7;
}
@@ -9,13 +9,14 @@ import {
} from "~/components/ui/dialog"
import { cn } from "~/lib/utils"
import { primaryGradientButtonClass } from "./buttonClasses"
/** 与系统管理弹窗 footer 一致的次要按钮样式 */
export const dialogSecondaryButtonClass =
"h-[37px] gap-[7px] rounded-md border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]"
/** 与系统管理弹窗 footer 一致的主按钮样式 */
export const dialogPrimaryButtonClass =
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]"
/** 与系统管理弹窗 footer 一致的主按钮样式STYLE_GUIDE §2.2 官方渐变) */
export const dialogPrimaryButtonClass = primaryGradientButtonClass
const EYEBROW_CLASS =
"text-[9px] font-extrabold tracking-[0.12em] text-[#2d82d4]"
@@ -59,13 +60,13 @@ function AppFormDialog({
<DialogContent
showCloseButton={showCloseButton}
className={cn(
"flex max-h-[80vh] flex-col gap-0 overflow-hidden rounded-[11px] border-[#dce4eb] bg-white p-0 text-[#1c2d42] shadow-[0_24px_70px_rgb(8_27_48/25%)] ring-0",
"flex max-h-[80vh] flex-col gap-0 overflow-hidden rounded-[11px] border-[#dce4eb] bg-white p-0 text-[#1c2d42] shadow-xl ring-0",
maxWidth,
contentClassName,
)}
>
{eyebrow || title ? (
<DialogHeader className="shrink-0 space-y-0 border-b border-[#e7ecf1] px-[22px] pb-4 pt-5 text-left">
<DialogHeader className="shrink-0 space-y-0 border-b border-line px-[22px] pb-4 pt-5 text-left">
{eyebrow ? <span className={EYEBROW_CLASS}>{eyebrow}</span> : null}
{title ? (
<DialogTitle id={titleId} className={TITLE_CLASS}>
@@ -78,7 +79,7 @@ function AppFormDialog({
{children}
</div>
{footer ? (
<DialogFooter className="mt-0 shrink-0 flex-row justify-end gap-2 border-t border-[#e8edf2] bg-white px-[22px] py-3.5 sm:justify-end">
<DialogFooter className="mt-0 shrink-0 flex-row justify-end gap-2 border-t border-line bg-white px-[22px] py-3.5 sm:justify-end">
{footer}
</DialogFooter>
) : null}
@@ -14,6 +14,7 @@ import {
dialogPrimaryButtonClass,
dialogSecondaryButtonClass,
} from "./AppFormDialog"
import { dangerSolidButtonClass } from "./buttonClasses"
type ConfirmDialogProps = {
open: boolean
@@ -58,7 +59,7 @@ function ConfirmDialog({
size="sm"
className={cn(
destructive
? "h-[37px] gap-[7px] rounded-md border-0 bg-[#d75b5b] px-[15px] text-xs font-[650] text-white hover:bg-[#a94f4f]"
? dangerSolidButtonClass
: dialogPrimaryButtonClass,
)}
onClick={() => void onConfirm()}
+4 -4
View File
@@ -54,15 +54,15 @@ export function Topbar({
<span
className={`size-[7px] rounded-full ${
apiOnline
? "bg-[#19b878] shadow-[0_0_0_3px_rgb(25_184_120_/_11%)]"
: "bg-[#d05151] shadow-[0_0_0_3px_rgb(208_81_81_/_10%)]"
? "bg-[#19b878] ring-3 ring-success/10"
: "bg-[#d05151] ring-3 ring-danger/10"
}`}
/>
{apiOnline ? "服务已连接" : "服务未连接"}
</div>
<div className="relative flex items-center gap-[9px]">
<button
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-[#e0e7ee] bg-white px-[11px] hover:border-[#b9cce0]"
className="flex h-[46px] cursor-pointer items-center gap-[9px] rounded-lg border border-line bg-white px-[11px] hover:border-[#b9cce0]"
type="button"
onClick={() => onSetWorkspaceMenuOpen(!workspaceMenuOpen)}
>
@@ -78,7 +78,7 @@ export function Topbar({
<ChevronRight size={15} />
</button>
{workspaceMenuOpen && (
<div className="absolute top-[calc(100%+7px)] right-0 z-50 w-[260px] rounded-lg border border-[#dce5ed] bg-white p-1.5 shadow-[0_14px_38px_rgb(17_43_70_/_18%)]">
<div className="absolute top-[calc(100%+7px)] right-0 z-50 w-[260px] rounded-lg border border-line bg-white p-1.5 shadow-lg">
{workspaces.map((workspace) => {
const isSelected =
workspace.workspace_id === currentWorkspace.workspace_id;
@@ -0,0 +1,12 @@
/**
* STYLE_GUIDE §2.2 官方按钮类。
* 收敛自 modalUi.primaryButtonClass / AppFormDialog.dialogPrimaryButtonClass /
* admin 三页 PRIMARY_BUTTON_CLASS 的重复定义,色值统一走 @theme token。
*/
export const primaryGradientButtonClass =
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-gap-icon rounded-md border border-brand bg-gradient-to-br from-brand to-brand-deep px-button-x text-xs font-medium-plus text-white shadow-md enabled:hover:from-brand-strong enabled:hover:to-brand-deep disabled:cursor-not-allowed disabled:opacity-[0.58]";
/** 危险确认按钮(ConfirmDialog destructive 态),token 化自原 bg-[#d75b5b] 写法 */
export const dangerSolidButtonClass =
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-gap-icon rounded-md border-0 bg-danger px-button-x text-xs font-medium-plus text-white enabled:hover:bg-danger-strong disabled:cursor-not-allowed disabled:opacity-[0.58]";
@@ -38,7 +38,7 @@ export function DashboardPage({
return (
<section className="h-full overflow-auto bg-bg p-6">
<div className="dashboard-hero-shell flex items-center justify-between rounded-xl px-[34px] py-[34px] text-white shadow-[0_12px_30px_rgb(19_80_137/18%)]">
<div className="dashboard-hero-shell flex items-center justify-between rounded-xl px-[34px] py-[34px] text-white shadow-lg">
<div>
<span className="text-[10px] tracking-[0.12em] opacity-80">
MODEL DEVELOPMENT PLATFORM
@@ -78,7 +78,7 @@ export function DashboardPage({
</span>
<span className="flex flex-col">
<b className="text-[21px] text-[#20364c]">{item.value}</b>
<small className="text-[#8796a6]">{item.label}</small>
<small className="text-ink-subtle">{item.label}</small>
</span>
</article>
))}
@@ -114,7 +114,7 @@ export function DashboardPage({
</div>
<div className="mt-[18px] grid gap-3.5 pb-[25px] max-[1200px]:grid-cols-1 min-[1201px]:grid-cols-[minmax(0,1.6fr)_minmax(310px,0.9fr)]">
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)]">
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-md">
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
<div>
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
@@ -136,7 +136,7 @@ export function DashboardPage({
{Math.round(value / 7)}
</span>
<i
className="block min-h-3 w-[min(36px,72%)] rounded-t-[5px] rounded-b-[2px] bg-gradient-to-b from-[#49a0eb] to-[#207bd1] shadow-[0_4px_9px_rgb(32_123_209/18%)]"
className="block min-h-3 w-[min(36px,72%)] rounded-t-[5px] rounded-b-[2px] bg-gradient-to-b from-[#49a0eb] to-[#207bd1] shadow-md"
style={{ height: `${value}%` }}
/>
<small className="text-[9px] text-[#7890a3]">
@@ -157,7 +157,7 @@ export function DashboardPage({
</footer>
</section>
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)]">
<section className="rounded-[10px] border border-[#dfe7ee] bg-white shadow-md">
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
<div>
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
@@ -170,7 +170,7 @@ export function DashboardPage({
<div className="dashboard-donut relative grid size-32 shrink-0 place-items-center rounded-full">
<span className="z-[1] flex flex-col items-center">
<b className="text-xl text-[#273d53]">{scriptCount}</b>
<small className="text-[8px] text-[#8a9aaa]"></small>
<small className="text-[8px] text-ink-subtle"></small>
</span>
</div>
<div className="flex flex-col gap-[15px]">
@@ -206,7 +206,7 @@ export function DashboardPage({
</div>
</section>
<section className="col-span-full rounded-[10px] border border-[#dfe7ee] bg-white shadow-[0_5px_16px_rgb(33_65_97/5%)] max-[1200px]:col-auto">
<section className="col-span-full rounded-[10px] border border-[#dfe7ee] bg-white shadow-md max-[1200px]:col-auto">
<header className="flex items-center justify-between border-b border-line-soft px-5 py-[17px]">
<div>
<span className="text-[9px] font-extrabold tracking-[0.09em] text-[#7890a6]">
@@ -67,7 +67,7 @@ export function ImportMemberDialog({
>
<div className={modalFormClass}>
<label className={`${formFieldClass} pb-[120px]`}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<UserMultiSelect
users={availableUsers}
selectedUserIds={selectedUserIds}
@@ -13,9 +13,9 @@ import {
} from "../platform/modalUi";
const FORM_INPUT_CLASS =
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
const FORM_TEXTAREA_CLASS =
"min-h-[92px] w-full resize-y rounded-md border border-[#d6e0e9] bg-white px-[11px] py-2.5 text-xs leading-[1.55] text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)]";
"min-h-[92px] w-full resize-y rounded-md border border-[#d6e0e9] bg-white px-[11px] py-2.5 text-xs leading-[1.55] text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10";
export function ProjectEditDialog({
open,
@@ -47,7 +47,7 @@ export function ProjectEditDialog({
<form className={modalFormClass} onSubmit={(event) => void onSubmit(event)}>
{!editingProject && (
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<Input
className={FORM_INPUT_CLASS}
autoFocus
@@ -58,7 +58,7 @@ export function ProjectEditDialog({
</label>
)}
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<Input
className={FORM_INPUT_CLASS}
value={projectForm.workspace_name}
@@ -86,7 +86,7 @@ export function ProjectEditDialog({
rows={4}
/>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -32,6 +32,8 @@ import {
statusPillClass,
} from "./adminUi";
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
const EMPTY_PROJECT_FORM = {
workspace_code: "",
workspace_name: "",
@@ -39,9 +41,6 @@ const EMPTY_PROJECT_FORM = {
description: "",
};
const PRIMARY_BUTTON_CLASS =
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
export function ProjectManagementPage({
onNotify,
onConnectionChange,
@@ -272,7 +271,7 @@ export function ProjectManagementPage({
type="button"
disabled={!canManage}
onClick={openCreateProject}
className={PRIMARY_BUTTON_CLASS}
className={primaryGradientButtonClass}
>
<Plus size={15} />
@@ -280,15 +279,15 @@ export function ProjectManagementPage({
</div>
{!canManage && <div className={adminReadonlyClass}></div>}
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
<AdminColgroup widths={USER_PROJECT_COL_WIDTHS} />
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -1,5 +1,6 @@
import { type Workspace, type WorkspaceMember } from "../../services/api";
import { Plus, X } from "lucide-react";
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
export function ProjectMembersDrawer({
open,
@@ -75,7 +76,7 @@ export function ProjectMembersDrawer({
{members.length}
</span>
<button
className="primary-button"
className={primaryGradientButtonClass}
type="button"
onClick={() => onAddMembers()}
style={{ fontSize: 13, padding: "6px 12px" }}
@@ -167,7 +167,7 @@ export function RoleEditDialog({
</label>
) : (
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<input
className={formInputClass}
autoFocus
@@ -182,7 +182,7 @@ export function RoleEditDialog({
</label>
)}
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<input
className={formInputClass}
value={form.role_name}
@@ -214,7 +214,7 @@ export function RoleEditDialog({
/>
</div>
)}
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -37,9 +37,7 @@ import {
rowButtonClass,
rowDangerButtonClass,
} from "./adminUi";
const PRIMARY_BUTTON_CLASS =
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
export function RoleManagementPage({
onNotify,
@@ -208,7 +206,7 @@ export function RoleManagementPage({
type="button"
disabled={!canManage}
onClick={openCreate}
className={PRIMARY_BUTTON_CLASS}
className={primaryGradientButtonClass}
>
<Plus size={15} />
@@ -220,15 +218,15 @@ export function RoleManagementPage({
)}
{loadError && <div className={adminReadonlyClass}>{loadError}</div>}
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
<AdminColgroup widths={ROLE_COL_WIDTHS} />
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -41,6 +41,8 @@ import {
statusPillClass,
} from "./adminUi";
import { primaryGradientButtonClass } from "~/components/common/buttonClasses";
const EMPTY_FORM = {
username: "",
display_name: "",
@@ -50,10 +52,8 @@ const EMPTY_FORM = {
status: "active" as "active" | "disabled" | "locked",
};
const PRIMARY_BUTTON_CLASS =
"h-[37px] gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] enabled:hover:from-[#1175d8] enabled:hover:to-[#0e5bad]";
const FORM_INPUT_CLASS =
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)] disabled:cursor-not-allowed disabled:opacity-50";
"h-[39px] w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10 disabled:cursor-not-allowed disabled:opacity-50";
export function UserManagementPage({
onNotify,
onConnectionChange,
@@ -216,7 +216,7 @@ export function UserManagementPage({
type="button"
disabled={!canManage}
onClick={openCreate}
className={PRIMARY_BUTTON_CLASS}
className={primaryGradientButtonClass}
>
<Plus size={15} />
@@ -224,15 +224,15 @@ export function UserManagementPage({
</div>
{!canManage && <div className={adminReadonlyClass}></div>}
<Table className="rounded-[9px] border border-[#dfe7ef] overflow-hidden text-[11px] text-[#44576a] [--border-color:#dfe7ef] table-fixed bg-white">
<Table className="rounded-[9px] border border-line overflow-hidden text-[11px] text-[#44576a] [--border-color:var(--color-line)] table-fixed bg-white">
<AdminColgroup widths={USER_PROJECT_COL_WIDTHS} />
<TableHeader className="bg-[#f5f8fb] text-[#748598]">
<TableHeader className="bg-[#f5f8fb] text-ink-caption">
<TableRow className="hover:bg-transparent border-b border-[#edf1f5]">
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-[#748598]"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
<TableHead className="h-10 px-4 text-[10px] font-bold text-ink-caption"></TableHead>
</TableRow>
</TableHeader>
<TableBody>
@@ -301,7 +301,7 @@ export function UserManagementPage({
>
<form className={modalFormClass} onSubmit={(event) => void submit(event)}>
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<Input
className={FORM_INPUT_CLASS}
autoFocus={!editing}
@@ -311,7 +311,7 @@ export function UserManagementPage({
/>
</label>
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<Input
className={FORM_INPUT_CLASS}
disabled={Boolean(editing)}
@@ -323,7 +323,7 @@ export function UserManagementPage({
</label>
{!editing && (
<label className={formFieldClass}>
<span><span className="text-[#e74c3c]">*</span></span>
<span><span className="text-danger">*</span></span>
<Input
className={FORM_INPUT_CLASS}
type="password"
@@ -381,7 +381,7 @@ export function UserManagementPage({
</select>
</label>
)}
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
+2 -2
View File
@@ -5,7 +5,7 @@ import { cn } from "~/lib/utils";
export const adminPageClass = "h-full overflow-auto bg-[#f3f6f9] p-6";
export const adminToolbarClass =
"mb-3.5 flex items-center justify-between rounded-lg border border-[#dfe7ef] bg-white px-3.5 py-3";
"mb-3.5 flex items-center justify-between rounded-lg border border-line bg-white px-3.5 py-3";
export const adminToolbarSearchClass =
"flex w-[280px] items-center gap-2 rounded-md border border-[#d9e2eb] bg-[#f8fafb] px-3 py-2 [&_svg]:text-[#8a99a8]";
@@ -16,7 +16,7 @@ export const adminToolbarInputClass =
export const adminReadonlyClass =
"mb-3 rounded-md border border-[#f1d79c] bg-[#fff8e8] px-3.5 py-2.5 text-[11px] text-[#8a6416]";
export const adminEmptyClass = "p-[25px] text-center text-[#8796a6]";
export const adminEmptyClass = "p-[25px] text-center text-ink-subtle";
export const employeeNameClass =
"flex items-center gap-2.5 [&_small]:mt-[3px] [&_small]:text-[#93a0ae] [&_strong]:text-[#23384e] [&>span]:flex [&>span]:min-w-0 [&>span]:flex-col";
@@ -1,5 +1,5 @@
import { type FormEvent } from "react";
import { Folder } from "lucide-react";
import { Folder, Loader2Icon } from "lucide-react";
import {
AppFormDialog,
dialogPrimaryButtonClass,
@@ -57,7 +57,7 @@ export function CreateFolderModal({
onChange={(event) => onNameChange(event.target.value)}
/>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -75,7 +75,7 @@ export function CreateFolderModal({
className={dialogPrimaryButtonClass}
>
{busy
? <span className="button-spinner" />
? <Loader2Icon className="size-4 animate-spin" />
: <Folder size={16} />}
{busy ? "正在创建…" : "创建文件夹"}
</Button>
@@ -1,5 +1,5 @@
import { type FormEvent } from "react";
import { BookOpen, Check, FileCode, Folder, Plus } from "lucide-react";
import { BookOpen, Check, FileCode, Folder, Loader2Icon, Plus } from "lucide-react";
import type { ScriptType, Visibility } from "../../services/api";
import {
AppFormDialog,
@@ -113,7 +113,7 @@ export function CreateScriptModal({
key={item.type}
className={`relative flex min-h-[72px] cursor-pointer items-center gap-[9px] rounded-[7px] border p-2.5 text-left ${
selected
? "border-[#4d98df] bg-[#f4f9ff] shadow-[0_0_0_2px_rgb(49_133_216/7%)]"
? "border-[#4d98df] bg-[#f4f9ff] ring-2 ring-brand/10"
: "border-[#dae2ea] bg-white hover:border-[#a9c6e1]"
}`}
type="button"
@@ -162,7 +162,7 @@ export function CreateScriptModal({
</select>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -179,7 +179,7 @@ export function CreateScriptModal({
disabled={creating || !form.name.trim()}
className={dialogPrimaryButtonClass}
>
{creating ? <span className="button-spinner" /> : <Plus size={16} />}
{creating ? <Loader2Icon className="size-4 animate-spin" /> : <Plus size={16} />}
{creating ? "正在创建…" : "创建脚本"}
</Button>
</div>
@@ -1,5 +1,5 @@
import { type FormEvent } from "react";
import { Upload } from "lucide-react";
import { Loader2Icon, Upload } from "lucide-react";
import type { Visibility } from "../../services/api";
import {
AppFormDialog,
@@ -121,7 +121,7 @@ export function DataResourceUploadModal({
onChange={(event) => onDescriptionChange(event.target.value)}
/>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -140,7 +140,7 @@ export function DataResourceUploadModal({
className={dialogPrimaryButtonClass}
>
{uploading ? (
<span className="button-spinner" />
<Loader2Icon className="size-4 animate-spin" />
) : (
<Upload size={16} />
)}
@@ -1,5 +1,5 @@
import { type FormEvent } from "react";
import { ArrowUpFromLine } from "lucide-react";
import { ArrowUpFromLine, Loader2Icon } from "lucide-react";
import type { ScriptItem, Visibility } from "../../services/api";
import { scriptIcon } from "../../features/platform/WorkspaceTree";
import {
@@ -97,7 +97,7 @@ export function PublishModal({
<option value="public"></option>
</select>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
variant="outline"
size="sm"
@@ -115,7 +115,7 @@ export function PublishModal({
className={dialogPrimaryButtonClass}
>
{publishing
? <span className="button-spinner" />
? <Loader2Icon className="size-4 animate-spin" />
: <ArrowUpFromLine size={16} />}
{publishing ? "正在发布…" : "确认发布"}
</Button>
@@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from "react";
import Editor from "@monaco-editor/react";
import { Info } from "lucide-react";
import { Info, Loader2Icon } from "lucide-react";
import type { ScriptItem } from "../../services/api";
@@ -68,11 +68,11 @@ export function PythonEditor({
return (
<div className="flex h-full min-h-0 flex-col bg-white" ref={containerRef}>
<section
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#dce5ed] bg-white/95 p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)]"
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-line bg-white/95 p-9 text-center text-[#66788d] shadow-md"
aria-live="polite"
>
<div className="mb-4 grid size-[54px] place-items-center rounded-full bg-[#edf6ff] text-[#247bc5]">
<span className="button-spinner button-spinner--blue size-6 border-[3px]" />
<Loader2Icon className="size-6 animate-spin text-brand" />
</div>
<strong className="text-base text-[#1d344d]"> Python </strong>
<p className="mt-[9px] max-w-[430px] text-xs leading-[1.7] text-[#7a8a9b]">
@@ -87,7 +87,7 @@ export function PythonEditor({
return (
<div className="flex h-full min-h-0 flex-col bg-white" ref={containerRef}>
<section
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#f0cfcc] bg-white/95 p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)]"
className="mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border border-[#f0cfcc] bg-white/95 p-9 text-center text-[#66788d] shadow-md"
aria-live="polite"
>
<div className="mb-4 grid size-[54px] place-items-center rounded-full bg-[#fff1f0] text-[#c84f49]">
@@ -163,8 +163,8 @@ export function ScriptExplorer({
}, [filteredScripts, directories, user, dataByOwner, members]);
return (
<aside className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-[0_2px_7px_rgb(25_46_73/3%)]">
<div className="flex min-h-16 items-center justify-between border-b border-[#e8edf2] px-3.5">
<aside className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-md">
<div className="flex min-h-16 items-center justify-between border-b border-line px-3.5">
<div>
<h2 className="mb-0.5 text-base text-[#1d2d42]"></h2>
</div>
@@ -197,7 +197,7 @@ export function ScriptExplorer({
</div>
</div>
<div className="mx-3 mt-[11px] mb-[7px] flex h-[38px] items-center gap-[7px] rounded-md border border-[#dce5ed] bg-[#fbfcfd] px-[9px] text-[#95a2b1] focus-within:border-[#6aa9e9] focus-within:shadow-[0_0_0_3px_rgb(31_119_207/8%)]">
<div className="mx-3 mt-[11px] mb-[7px] flex h-[38px] items-center gap-[7px] rounded-md border border-line bg-[#fbfcfd] px-[9px] text-[#95a2b1] focus-within:border-[#6aa9e9] focus-within:ring-3 focus-within:ring-brand/10">
<Search size={17} />
<input
className="min-w-0 flex-1 border-0 bg-transparent text-xs text-[#26384d] outline-none placeholder:text-[#a7b2bf]"
@@ -5,6 +5,7 @@ import {
ExternalLink,
FileCode,
Info,
Loader2Icon,
Lock,
Plus,
RefreshCw,
@@ -504,7 +505,7 @@ export function ScriptWorkspace({
onClick={() => onSavePythonEditor(script.script_id)}
>
{activePythonBuf?.saving
? <><span className="button-spinner button-spinner--blue size-3" /> </>
? <><Loader2Icon className="size-3 animate-spin text-brand" /> </>
: "保存"}
</button>
)}
@@ -592,7 +593,7 @@ export function ScriptWorkspace({
<div className="min-h-0 flex-1 overflow-y-auto">
{readOnlyLoading ? (
<div className="flex min-h-[200px] flex-col items-center justify-center gap-4 text-sm text-[#666]">
<span className="button-spinner button-spinner--blue" />
<Loader2Icon className="size-4 animate-spin text-brand" />
<span>...</span>
</div>
) : readOnlyError ? (
@@ -683,7 +684,7 @@ export function ScriptWorkspace({
>
<div className="flex min-h-[34px] items-center gap-[18px] border-b border-[#dfe6ec] bg-[#f8fafc] px-3.5 text-[9px] text-[#718195]">
<span className="inline-flex items-center gap-1.5 font-[650] text-[#197b59]">
<i className="size-[7px] rounded-full bg-[#20b77d] shadow-[0_0_0_3px_rgb(32_183_125/12%)]" />
<i className="size-[7px] rounded-full bg-[#20b77d] ring-3 ring-success/10" />
Workspace Jupyter Server
</span>
<span className="inline-flex items-center gap-1.5">
@@ -709,10 +710,10 @@ export function ScriptWorkspace({
{!sessionCache.has(script.script_id) ? (
isNotebook ? (
<section
className={`mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border p-9 text-center text-[#66788d] shadow-[0_12px_34px_rgb(31_64_98/7%)] ${
className={`mx-auto my-16 flex w-[min(520px,calc(100%-48px))] min-h-[260px] flex-col items-center justify-center rounded-[10px] border p-9 text-center text-[#66788d] shadow-md ${
openError
? "border-[#f0cfcc] bg-white/95"
: "border-[#dce5ed] bg-white/95"
: "border-line bg-white/95"
}`}
aria-live="polite"
>
@@ -725,7 +726,7 @@ export function ScriptWorkspace({
>
{openError
? <Info size={28} />
: <span className="button-spinner button-spinner--blue size-6 border-[3px]" />}
: <Loader2Icon className="size-6 animate-spin text-brand" />}
</div>
<strong className="text-base text-[#1d344d]">
{openError ? "Notebook 打开失败" : "正在打开 Notebook"}
@@ -743,7 +744,7 @@ export function ScriptWorkspace({
onClick={onOpenEditor}
>
{editBusy
? <span className="button-spinner button-spinner--blue" />
? <Loader2Icon className="size-4 animate-spin text-brand" />
: <RefreshCw size={16} />}
{editBusy ? "正在重试…" : "重试打开"}
</button>
@@ -751,7 +752,7 @@ export function ScriptWorkspace({
</section>
) : isPython && !activePythonBuf ? (
<section className="mx-auto mb-[38px] mt-6 w-[min(1020px,calc(100%-48px))] max-xl:w-[calc(100%-30px)]">
<div className="flex items-start justify-between gap-5 rounded-t-[9px] border border-[#dce5ed] bg-white px-[26px] py-[23px]">
<div className="flex items-start justify-between gap-5 rounded-t-[9px] border border-line bg-white px-[26px] py-[23px]">
<div>
<span className="text-[9px] font-extrabold tracking-[0.13em] text-[#2b7fca]">
PYTHON SCRIPT
@@ -774,7 +775,7 @@ export function ScriptWorkspace({
onClick={onOpenEditor}
>
{editBusy
? <span className="button-spinner button-spinner--blue" />
? <Loader2Icon className="size-4 animate-spin text-brand" />
: <ExternalLink size={17} />}
{editBusy
? "正在准备编辑器…"
@@ -784,7 +785,7 @@ export function ScriptWorkspace({
</button>
</div>
<div className="grid grid-cols-4 border-x border-b border-[#dce5ed] bg-[#f9fbfd]">
<div className="grid grid-cols-4 border-x border-b border-line bg-[#f9fbfd]">
{[
["脚本类型", "Python"],
[
@@ -804,12 +805,12 @@ export function ScriptWorkspace({
index < 3 ? "border-r border-[#e4eaf0]" : ""
}`}
>
<span className="text-[10px] text-[#8a9aaa]">{label}</span>
<span className="text-[10px] text-ink-subtle">{label}</span>
<strong className="text-[13px] text-[#2a3f55]">{value}</strong>
</div>
))}
</div>
<div className="overflow-hidden rounded-b-[9px] border border-t-0 border-[#dce5ed] bg-white">
<div className="overflow-hidden rounded-b-[9px] border border-t-0 border-line bg-white">
<div className="flex items-center justify-between border-b border-[#e8edf3] bg-[#f7f9fb] px-3.5 py-2 text-[11px] text-[#6b7c8f]">
<div className="flex items-center gap-1.5">
<span className="size-2.5 rounded-full bg-[#ef6b62]" />
@@ -323,7 +323,7 @@ export default function ScriptsPage() {
onHandleUpload={handleUpload}
/>
<section className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-[0_2px_7px_rgb(25_46_73/3%)]">
<section className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-[#dce4ec] bg-white shadow-md">
{selected ? (
<ScriptWorkspace
script={selected}
@@ -59,7 +59,7 @@ export function TreeContextMenu({
return (
<div
className="fixed z-[1000] flex w-[180px] flex-col rounded-lg border border-[#d9e2ea] bg-white p-1.5 shadow-[0_14px_34px_rgb(18_43_68/20%)]"
className="fixed z-[1000] flex w-[180px] flex-col rounded-lg border border-[#d9e2ea] bg-white p-1.5 shadow-lg"
role="menu"
style={{
left: contextMenu.x,
@@ -179,7 +179,7 @@ export function TreeContextMenu({
</button>
{contextMenu.kind === "directory" && (
<>
<span className="mx-[3px] my-[5px] h-px bg-[#e7ecf1]" />
<span className="mx-[3px] my-[5px] h-px bg-line-soft" />
<button
className={dangerItemClass}
type="button"
@@ -29,7 +29,7 @@ export function VersionReceiptModal({
contentClassName="sm:max-w-[min(470px,calc(100vw-40px))] px-7 py-[30px] pb-[25px] text-center"
bodyClassName="overflow-visible"
>
<div className="mx-auto mb-3.5 grid size-[58px] place-items-center rounded-full bg-gradient-to-br from-[#24b67d] to-[#149566] text-white shadow-[0_9px_24px_rgb(25_157_104/25%)]">
<div className="mx-auto mb-3.5 grid size-[58px] place-items-center rounded-full bg-gradient-to-br from-[#24b67d] to-[#149566] text-white shadow-lg">
<Check size={28} />
</div>
<span className={modalEyebrowClass}>STABLE VERSION</span>
@@ -9,7 +9,7 @@ type WelcomePanelProps = {
export function WelcomePanel({ onCreateScript }: WelcomePanelProps) {
return (
<div className="welcome-panel-shell flex min-h-full flex-col items-center justify-center p-[35px] text-center">
<div className="mb-[21px] grid size-[86px] place-items-center rounded-[25px] border border-[#cfe0f0] bg-gradient-to-br from-white to-[#eaf4fd] text-[#3d8cd9] shadow-[0_16px_35px_rgb(40_103_165/12%)]">
<div className="mb-[21px] grid size-[86px] place-items-center rounded-[25px] border border-[#cfe0f0] bg-gradient-to-br from-white to-[#eaf4fd] text-[#3d8cd9] shadow-lg">
<SquareChartGantt size={42}/>
</div>
<span className="text-[10px] font-extrabold tracking-[0.12em] text-[#2e81ce]">
@@ -302,7 +302,7 @@ function WorkspaceTreeItems({
)}
{item.visibility !== "private" && (
<span
className="size-1.5 shrink-0 rounded-full bg-[#1dbd7c] shadow-[0_0_0_3px_rgb(29_189_124/10%)]"
className="size-1.5 shrink-0 rounded-full bg-[#1dbd7c] ring-3 ring-success/10"
title="Workspace 可见"
/>
)}
+7 -8
View File
@@ -4,13 +4,13 @@ export const modalBackdropClass =
"fixed inset-0 z-20 grid place-items-center bg-[rgb(10_27_45/43%)] backdrop-blur-[2px]";
export const modalPanelClass =
"modal-panel w-[min(520px,calc(100vw-40px))] overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-[0_24px_70px_rgb(8_27_48/25%)]";
"modal-panel w-[min(520px,calc(100vw-40px))] overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-xl";
export const modalCompactClass =
"modal-panel flex max-h-[80vh] w-[min(480px,calc(100vw-40px))] flex-col overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-[0_24px_70px_rgb(8_27_48/25%)]";
"modal-panel flex max-h-[80vh] w-[min(480px,calc(100vw-40px))] flex-col overflow-visible rounded-[11px] border border-[#dce4eb] bg-white shadow-xl";
export const modalHeaderClass =
"flex shrink-0 items-center justify-between border-b border-[#e7ecf1] px-[22px] pb-4 pt-5";
"flex shrink-0 items-center justify-between border-b border-line px-[22px] pb-4 pt-5";
export const modalEyebrowClass =
"text-[9px] font-extrabold tracking-[0.12em] text-[#2d82d4]";
@@ -20,13 +20,12 @@ export const modalTitleClass = "mt-1 text-[19px] text-[#1c2d42]";
export const modalFormClass = "flex-1 overflow-y-auto px-[22px] pt-[19px]";
export const modalFooterClass =
"mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white rounded-b-[11px] px-[22px] py-3.5";
"mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white rounded-b-[11px] px-[22px] py-3.5";
export const iconButtonClass =
"grid size-[34px] cursor-pointer place-items-center rounded-[7px] border border-[#dfe6ed] bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]";
"grid size-[34px] cursor-pointer place-items-center rounded-[7px] border border-line bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]";
export const primaryButtonClass =
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-[7px] rounded-md border border-[#126ac3] bg-gradient-to-br from-[#1881e7] to-[#1268c9] px-[15px] text-xs font-[650] text-white shadow-[0_6px_15px_rgb(20_111_202/18%)] hover:from-[#1175d8] hover:to-[#0e5bad] disabled:cursor-not-allowed disabled:opacity-[0.58]";
export { primaryGradientButtonClass as primaryButtonClass } from "~/components/common/buttonClasses";
export const secondaryButtonClass =
"inline-flex min-h-[37px] cursor-pointer items-center justify-center gap-[7px] rounded-md border border-[#d8e1e9] bg-white px-[15px] text-xs font-[650] text-[#56687c]";
@@ -38,7 +37,7 @@ export const formFieldClass =
"mb-[17px] flex flex-col gap-[7px] text-xs font-[650] text-[#3c4e62]";
export const formControlClass =
"w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:shadow-[0_0_0_3px_rgb(38_125_207/8%)]";
"w-full rounded-md border border-[#d6e0e9] bg-white px-[11px] text-xs text-[#283a4e] outline-none focus:border-[#5b9ddb] focus:ring-3 focus:ring-brand/10";
export const formInputClass = `${formControlClass} h-[39px]`;
@@ -74,7 +74,7 @@ function ArtifactListImpl({
</span>
</div>
</div>
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-[#dce5ed] bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]">
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-line bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:ring-3 focus-within:ring-brand/10">
<Search size={15} />
<input
className="min-w-0 flex-1 border-0 bg-transparent text-[12px] text-[#33475d] outline-none placeholder:text-[#a6b2be]"
@@ -95,7 +95,7 @@ function ArtifactListImpl({
return (
<div
key={scriptName}
className="mb-1.5 overflow-hidden rounded-[8px] border border-line-soft bg-white transition-[border-color,box-shadow] duration-[160ms] hover:border-[#e1e8ef]"
className="mb-1.5 overflow-hidden rounded-[8px] border border-line-soft bg-white transition-[border-color,box-shadow] duration-150 hover:border-[#e1e8ef]"
>
<button
type="button"
@@ -106,7 +106,7 @@ function ArtifactListImpl({
<span className="flex min-w-0 items-center gap-[7px]">
<span
data-state={isExpanded ? "open" : "closed"}
className="h-[14px] w-[14px] flex-none text-[#9aa7b5] transition-transform duration-[180ms] data-[state=open]:rotate-90"
className="h-[14px] w-[14px] flex-none text-[#9aa7b5] transition-transform duration-200 data-[state=open]:rotate-90"
>
<ChevronRight size={14} />
</span>
@@ -140,7 +140,7 @@ function ArtifactListImpl({
<div className="flex flex-col gap-[5px] border-t border-line-soft bg-[#f8fafc] p-[7px_7px_7px_27px] min-[1281px]:pl-[29px]">
{items.map((artifact) => (
<div
className="group flex min-h-[34px] cursor-grab select-none items-center justify-between gap-[7px] rounded-[6px] border border-[#e4e9ee] bg-white p-1.5 transition-[border-color,background-color] duration-[140ms] hover:border-[#a8c8e9] hover:bg-[#fbfdff] active:cursor-grabbing min-[1281px]:px-[7px]"
className="group flex min-h-[34px] cursor-grab select-none items-center justify-between gap-[7px] rounded-[6px] border border-[#e4e9ee] bg-white p-1.5 transition-[border-color,background-color] duration-150 hover:border-[#a8c8e9] hover:bg-[#fbfdff] active:cursor-grabbing min-[1281px]:px-[7px]"
draggable
key={artifact.versions_id}
onDragStart={(event) => {
@@ -20,7 +20,7 @@ export function NodeInspector({
}) {
return (
<div className="flex min-h-full flex-col">
<section className="flex flex-row items-center gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-row items-center gap-[10px] border-b border-line p-[14px]">
<span
className={`grid h-[31px] w-[31px] flex-none place-items-center rounded-[6px] ${
node.version.script_type === "notebook"
@@ -41,7 +41,7 @@ export function NodeInspector({
</strong>
</div>
</section>
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<div className="flex flex-col gap-[5px] rounded-[6px] border border-[#dae6f2] bg-[#f6faff] p-[9px]">
<strong className="text-[11px] text-[#35516e]">{node.version.script_name}</strong>
@@ -56,12 +56,12 @@ export function NodeInspector({
</small>
</div>
</section>
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.nodeName}
onChange={(event) => onChange({
...form,
@@ -71,11 +71,11 @@ export function NodeInspector({
</label>
<div className="grid grid-cols-2 gap-2">
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
type="number"
min="1"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.timeoutSeconds}
onChange={(event) => onChange({
...form,
@@ -84,12 +84,12 @@ export function NodeInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
type="number"
min="0"
max="10"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.retryCount}
onChange={(event) => onChange({
...form,
@@ -100,11 +100,11 @@ export function NodeInspector({
</div>
<div className="grid grid-cols-2 gap-2">
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
type="number"
min="0"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.retryIntervalSec}
onChange={(event) => onChange({
...form,
@@ -113,9 +113,9 @@ export function NodeInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]">Python </span>
<span className="text-[10px] font-semibold text-ink-caption">Python </span>
<select
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.pythonVersion}
onChange={(event) => onChange({
...form,
@@ -131,12 +131,12 @@ export function NodeInspector({
</label>
</div>
</section>
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]">arguments_json</span>
<span className="text-[10px] font-semibold text-ink-caption">arguments_json</span>
<textarea
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
rows={5}
spellCheck={false}
value={form.argumentsJson}
@@ -147,9 +147,9 @@ export function NodeInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]">env_refs_json</span>
<span className="text-[10px] font-semibold text-ink-caption">env_refs_json</span>
<textarea
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="w-full resize-y rounded-[5px] border border-line p-[7px_8px] font-mono text-[10px] leading-[1.5] !bg-[#f8fafc] !text-[#37516a] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
rows={4}
spellCheck={false}
value={form.envRefsJson}
@@ -216,8 +216,8 @@ export function RunHistory({
};
return (
<section className="flex min-h-0 flex-col border-t border-[#e0e7ee] bg-[#fbfcfe]">
<header className="flex min-h-[44px] items-center justify-between border-b border-[#e8edf2] bg-white px-3">
<section className="flex min-h-0 flex-col border-t border-line bg-[#fbfcfe]">
<header className="flex min-h-[44px] items-center justify-between border-b border-line bg-white px-3">
<div className="flex items-center gap-[7px]">
<strong className="text-[12px] text-[#2b4056]"></strong>
<span className="inline-grid h-[18px] min-w-[18px] place-items-center rounded-[9px] bg-[#edf2f7] text-[9px] text-[#71849a]">
@@ -250,7 +250,7 @@ export function RunHistory({
return (
<article
data-state={expanded ? "expanded" : "collapsed"}
className="flex-none overflow-hidden rounded-[6px] border border-[#e1e8ef] bg-white data-[state=expanded]:border-[#b9d6f1] data-[state=expanded]:shadow-[0_3px_10px_rgb(38_117_185_/_8%)]"
className="flex-none overflow-hidden rounded-[6px] border border-[#e1e8ef] bg-white data-[state=expanded]:border-[#b9d6f1] data-[state=expanded]:shadow-md"
key={run.run_id}
>
<div
@@ -295,7 +295,7 @@ export function RunHistory({
onClick={() => toggleRun(run.run_id)}
/>
<section
className="fixed left-1/2 top-1/2 z-[41] grid max-h-[min(760px,calc(100vh-64px))] w-[min(780px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-[12px] border border-[#d8e2eb] bg-white shadow-[0_24px_80px_rgb(8_27_48_/_30%)] [grid-template-rows:auto_minmax(0,1fr)]"
className="fixed left-1/2 top-1/2 z-[41] grid max-h-[min(760px,calc(100vh-64px))] w-[min(780px,calc(100vw-48px))] -translate-x-1/2 -translate-y-1/2 overflow-hidden rounded-[12px] border border-[#d8e2eb] bg-white shadow-xl [grid-template-rows:auto_minmax(0,1fr)]"
role="dialog"
aria-modal="true"
aria-label="调度运行结果"
@@ -311,7 +311,7 @@ export function RunHistory({
</code>
</div>
<button
className="grid h-[34px] w-[34px] cursor-pointer place-items-center rounded-[7px] border border-[#dfe6ed] bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]"
className="grid h-[34px] w-[34px] cursor-pointer place-items-center rounded-[7px] border border-line bg-white hover:border-[#b9c9da] hover:bg-[#f7faff]"
type="button"
aria-label="关闭运行结果"
onClick={() => toggleRun(run.run_id)}
@@ -321,13 +321,13 @@ export function RunHistory({
</header>
<div className="min-h-0 overflow-auto bg-[#f8fbfe] p-[18px]">
{detailLoadingId === run.run_id && !detail ? (
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6]">
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle">
</p>
) : detailErrors[run.run_id] ? (
<p
data-state="error"
className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6] data-[state=error]:text-[#bf5656]"
className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle data-[state=error]:text-[#bf5656]"
>
{detailErrors[run.run_id]}
</p>
@@ -369,7 +369,7 @@ export function RunHistory({
)}
<div className="flex flex-col gap-2.5">
{detail.node_runs.length === 0 ? (
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-[#8796a6]">
<p className="m-0 p-[8px_4px] text-center text-[9px] leading-[1.5] text-ink-subtle">
</p>
) : detail.node_runs.map((nodeRun) => {
@@ -43,12 +43,12 @@ export function ScheduleInspector({
}
return (
<div className="flex min-h-full flex-col">
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.scheduleName}
onChange={(event) => onChange({
...form,
@@ -57,9 +57,9 @@ export function ScheduleInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<textarea
className="w-full resize-y rounded-[5px] border border-line bg-white p-[7px_8px] text-[11px] leading-[1.5] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="w-full resize-y rounded-[5px] border border-line bg-white p-[7px_8px] text-[11px] leading-[1.5] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
rows={2}
value={form.description}
onChange={(event) => onChange({
@@ -85,12 +85,12 @@ export function ScheduleInspector({
</label>
</section>
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<select
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.triggerType}
onChange={(event) => onChange({
...form,
@@ -106,9 +106,9 @@ export function ScheduleInspector({
{form.triggerType === "cron" && (
<>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]">Cron </span>
<span className="text-[10px] font-semibold text-ink-caption">Cron </span>
<input
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.cronExpression}
onChange={(event) => onChange({
...form,
@@ -117,9 +117,9 @@ export function ScheduleInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.timezone}
onChange={(event) => onChange({
...form,
@@ -150,16 +150,16 @@ export function ScheduleInspector({
)}
</section>
<section className="flex flex-col gap-[10px] border-b border-[#e7ecf1] p-[14px]">
<section className="flex flex-col gap-[10px] border-b border-line p-[14px]">
<h3 className="mb-[2px] text-[13px] text-[#273b50]"></h3>
<div className="grid grid-cols-2 gap-2">
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<input
type="number"
min="1"
max="64"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.maxConcurrency}
onChange={(event) => onChange({
...form,
@@ -168,9 +168,9 @@ export function ScheduleInspector({
/>
</label>
<label className="flex flex-col gap-[5px]">
<span className="text-[10px] font-semibold text-[#758497]"></span>
<span className="text-[10px] font-semibold text-ink-caption"></span>
<select
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-[#81b2e7] focus:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]"
className="h-[33px] w-full rounded-[5px] border border-line bg-white px-2 text-[11px] text-[#32475d] outline-none focus:border-brand/70 focus:ring-3 focus:ring-brand/10"
value={form.failurePolicy}
onChange={(event) => onChange({
...form,
@@ -35,7 +35,7 @@ export function ScheduleList({
</span>
</div>
</div>
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-[#dce5ed] bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:shadow-[0_0_0_3px_rgb(38_124_216_/_8%)]">
<label className="mx-[10px] mb-[7px] flex min-h-[35px] items-center gap-[7px] rounded-[6px] border border-line bg-white px-[9px] text-[#95a3b2] focus-within:border-[#8db9e8] focus-within:ring-3 focus-within:ring-brand/10">
<Search size={15} />
<input
className="min-w-0 flex-1 border-0 bg-transparent text-[12px] text-[#33475d] outline-none placeholder:text-[#a6b2be]"
@@ -64,7 +64,7 @@ export function ScheduleList({
>
<span
data-state={item.enabled ? "enabled" : "disabled"}
className="h-2 w-2 flex-none rounded-full bg-[#aeb9c5] shadow-[0_0_0_3px_rgb(142_156_171_/_11%)] data-[state=enabled]:bg-success data-[state=enabled]:shadow-[0_0_0_3px_rgb(24_185_121_/_12%)]"
className="h-2 w-2 flex-none rounded-full bg-[#aeb9c5] ring-3 ring-ink-subtle/15 data-[state=enabled]:bg-success data-[state=enabled]:ring-3 data-[state=enabled]:ring-success/10"
/>
<span className="flex min-w-0 flex-col gap-1">
<strong className="max-w-[205px] truncate text-[12px] text-[#283b50]">
@@ -14,6 +14,7 @@ import {
CalendarClock,
Check,
FileCode2,
Loader2Icon,
PackageOpen,
Play,
Plus,
@@ -367,7 +368,7 @@ export default function SchedulePage({
return (
<section className="relative flex min-h-0 flex-1 flex-col gap-[10px] overflow-hidden p-[10px]">
<header className="flex min-h-[56px] items-center justify-between rounded-[7px] border border-line bg-white px-[14px] shadow-[0_2px_7px_rgb(25_46_73_/_3%)]">
<header className="flex min-h-[56px] items-center justify-between rounded-[7px] border border-line bg-white px-[14px] shadow-md">
<div className="flex items-baseline gap-[10px]">
<strong className="text-[16px] text-[#1c2d42]"></strong>
<span className="hidden text-[11px] text-[#8b99a9] 2xl:inline">
@@ -423,7 +424,7 @@ export default function SchedulePage({
</header>
<div className="grid min-h-0 flex-1 gap-[10px] [grid-template-columns:225px_minmax(360px,1fr)_270px] min-[1281px]:[grid-template-columns:250px_minmax(380px,1fr)_292px] 2xl:[grid-template-columns:278px_minmax(420px,1fr)_316px]">
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)] [grid-template-rows:minmax(210px,42%)_minmax(260px,58%)]">
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-md [grid-template-rows:minmax(210px,42%)_minmax(260px,58%)]">
<ScheduleList
schedules={filteredSchedules}
selectedScheduleId={schedule?.schedule_id ?? null}
@@ -443,7 +444,7 @@ export default function SchedulePage({
/>
</aside>
<main className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)]">
<main className="flex min-h-0 flex-col overflow-hidden rounded-[7px] border border-line bg-white shadow-md">
<ScheduleCanvasHeader
linkSourceId={linkSourceId}
onCancelLink={() => setLinkSourceId(null)}
@@ -510,7 +511,7 @@ export default function SchedulePage({
{schedule?.nodes.map((node) => (
<div
className={`schedule-node absolute z-[2] flex cursor-grab touch-none select-none flex-col rounded-[8px] border border-[#91b7de] bg-white p-[11px_12px_9px] text-[#34516f] shadow-[0_7px_18px_rgb(36_75_117_/_11%)] active:cursor-grabbing data-[state=selected]:border-brand data-[state=selected]:shadow-[0_0_0_3px_rgb(25_120_212_/_11%),0_8px_22px_rgb(36_75_117_/_13%)]`}
className={`schedule-node absolute z-[2] flex cursor-grab touch-none select-none flex-col rounded-[8px] border border-[#91b7de] bg-white p-[11px_12px_9px] text-[#34516f] shadow-md active:cursor-grabbing data-[state=selected]:border-brand data-[state=selected]:ring-3 data-[state=selected]:ring-brand/10 data-[state=selected]:shadow-md`}
data-state={selectedNodeId === node.node_id ? "selected" : "idle"}
key={node.node_id}
style={{
@@ -529,7 +530,7 @@ export default function SchedulePage({
})}
>
<button
className="absolute left-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)]"
className="absolute left-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:ring-4 hover:ring-brand/15"
type="button"
aria-label={`连接到${node.node_name}`}
title={linkSourceId ? "点击完成连线" : "输入端口"}
@@ -580,7 +581,7 @@ export default function SchedulePage({
<b className="text-[10px] text-brand">{node.version.version_label}</b>
</footer>
<button
className="absolute right-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)] data-[state=active]:border-[#176ec5] data-[state=active]:bg-[#4e92db] data-[state=active]:shadow-[0_0_0_4px_rgb(45_124_207_/_16%)]"
className="absolute right-[-8px] top-[calc(50%-7px)] z-[4] h-[14px] w-[14px] cursor-crosshair rounded-full border-2 border-[#4e92db] bg-white p-0 hover:border-[#176ec5] hover:bg-[#4e92db] hover:ring-4 hover:ring-brand/15 data-[state=active]:border-[#176ec5] data-[state=active]:bg-[#4e92db] data-[state=active]:ring-4 data-[state=active]:ring-brand/15"
data-state={linkSourceId === node.node_id ? "active" : "idle"}
type="button"
aria-label={`${node.node_name}开始连线`}
@@ -647,7 +648,7 @@ export default function SchedulePage({
</footer>
</main>
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-[0_2px_7px_rgb(25_46_73_/_3%)] [grid-template-rows:repeat(2,minmax(0,1fr))]">
<aside className="grid min-h-0 overflow-hidden rounded-[7px] border border-line bg-white shadow-md [grid-template-rows:repeat(2,minmax(0,1fr))]">
<div className="min-h-0 overflow-auto">
{selectedNode ? (
<NodeInspector
@@ -682,7 +683,7 @@ export default function SchedulePage({
{contextMenu && (
<div
className="fixed z-[60] min-w-[168px] rounded-[8px] border border-[#d9e3ec] bg-white p-[5px] shadow-[0_14px_36px_rgb(14_35_57_/_22%)]"
className="fixed z-[60] min-w-[168px] rounded-[8px] border border-[#d9e3ec] bg-white p-[5px] shadow-lg"
role="menu"
style={{ left: contextMenu.x, top: contextMenu.y }}
onPointerDown={(event) => event.stopPropagation()}
@@ -783,7 +784,7 @@ export default function SchedulePage({
onFocus={(event) => event.currentTarget.select()}
/>
</label>
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-[#e8edf2] bg-white -mx-[22px] px-[22px] py-3.5">
<div className="mt-[3px] flex shrink-0 justify-end gap-2 border-t border-line bg-white -mx-[22px] px-[22px] py-3.5">
<Button
type="button"
variant="outline"
@@ -802,7 +803,7 @@ export default function SchedulePage({
className={dialogPrimaryButtonClass}
>
{busy === "create-schedule"
? <span className="button-spinner" />
? <Loader2Icon className="size-4 animate-spin" />
: <Plus size={15} />}
{busy === "create-schedule" ? "正在创建…" : "创建调度"}
</Button>
@@ -812,7 +813,7 @@ export default function SchedulePage({
{busy && (
<div
className="absolute bottom-[22px] right-[22px] z-[20] flex min-h-[38px] items-center gap-2 rounded-[7px] border border-[#c9dcee] bg-white px-[13px] text-[11px] text-[#4e657d] shadow-[0_8px_25px_rgb(28_56_86_/_15%)]"
className="absolute bottom-[22px] right-[22px] z-[20] flex min-h-[38px] items-center gap-2 rounded-[7px] border border-[#c9dcee] bg-white px-[13px] text-[11px] text-[#4e657d] shadow-lg"
aria-live="polite"
>
<span className="h-[15px] w-[15px] rounded-full border-2 border-[#b9d4ef] border-t-brand" />
+3 -3
View File
@@ -38,7 +38,7 @@ export default function LoginRoute() {
<main className="flex min-h-screen items-center justify-center bg-[radial-gradient(circle_at_20%_15%,rgba(22,119,255,0.12),transparent_34%),linear-gradient(145deg,#eef5fb,#f8fafc_55%,#edf3f8)] p-12">
<section
aria-labelledby="login-title"
className="w-[420px] rounded-[18px] border border-[#dbe6ef] bg-white/[0.96] p-[42px] shadow-[0_22px_60px_rgba(37,63,88,0.14)]"
className="w-[420px] rounded-[18px] border border-[#dbe6ef] bg-white/[0.96] p-[42px] shadow-xl"
>
<div
aria-hidden="true"
@@ -65,7 +65,7 @@ export default function LoginRoute() {
onChange={(event) => setUsername(event.target.value)}
placeholder="请输入用户名"
disabled={busy}
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:shadow-[0_0_0_3px_rgba(22,119,255,0.12)] disabled:opacity-65 disabled:cursor-not-allowed"
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:ring-3 focus:ring-brand/10 disabled:opacity-65 disabled:cursor-not-allowed"
/>
</label>
<label className="grid gap-2 text-[13px] text-[#465b70]">
@@ -77,7 +77,7 @@ export default function LoginRoute() {
onChange={(event) => setPassword(event.target.value)}
placeholder="请输入密码"
disabled={busy}
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:shadow-[0_0_0_3px_rgba(22,119,255,0.12)] disabled:opacity-65 disabled:cursor-not-allowed"
className="w-full rounded-[9px] border border-[#cad8e5] bg-white px-[14px] py-[12px] text-[#18334f] outline-none placeholder:text-[#728398] focus:border-[#1677ff] focus:ring-3 focus:ring-brand/10 disabled:opacity-65 disabled:cursor-not-allowed"
/>
</label>
{error && (