导出面板改用平台主题变量

- `Shell` 的米色页面渐变改用 `bg-(image:--platform-body-fill)`
- 文案 / 强调 / 描边换成 `--platform-text-strong` / `-base` / `-soft` / `-muted`、`--platform-accent`、`--platform-surface-border` 与 `--platform-surface-hover-border`
- 卡片与输入底色换成 `--platform-panel-fill` / `--platform-input-fill` / `--platform-neutral-bg`,冲突卡底换成 `--platform-warm-bg`
- 硬编码阴影换成 `--platform-panel-shadow` 与 `--platform-profile-action-shadow`
- 小红书品牌红 `#ff2442` 不在 `--platform-*` 调色板里,按原值保留并加注释
This commit is contained in:
2026-10-06 22:28:11 +08:00
parent a6974f3ca4
commit fafc83f9e6
14 changed files with 82 additions and 52 deletions
@@ -9,7 +9,7 @@ import { type Target, TARGET_META } from '../model';
export function EmptyTab({ target }: { target: Target }) {
return (
<div
className="grid h-full min-h-0 place-items-center p-6 text-sm text-[#988476]"
className="grid h-full min-h-0 place-items-center p-6 text-sm text-(--platform-text-soft)"
aria-label={`${TARGET_META[target].label}导出页`}
>
{`${TARGET_META[target].label}导出页尚未接入`}
@@ -22,8 +22,8 @@ export function Shell({
onClose: () => void;
}) {
return (
<main className="relative flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-gradient-to-b from-[#fffdf9] via-[#fdf9f5] to-[#f8efe7] text-[#3d1f10]">
<header className="flex shrink-0 items-stretch gap-3 border-b border-[#e2cbb8] px-4">
<main className="relative flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-(image:--platform-body-fill) text-(--platform-text-strong)">
<header className="flex shrink-0 items-stretch gap-3 border-b border-(--platform-surface-border) px-4">
<div className="flex items-center py-2">
<strong className="text-base font-semibold">导出到</strong>
</div>
@@ -35,7 +35,7 @@ export function Shell({
type="button"
aria-label="关闭导出面板"
title="关闭"
className="grid size-8 shrink-0 place-items-center rounded-lg border border-[#e2cbb8] bg-[#fffaf5] text-[#6f5848] transition hover:border-[#c7653d] hover:text-[#c7653d]"
className="grid size-8 shrink-0 place-items-center rounded-lg border border-(--platform-surface-border) bg-(image:--platform-panel-fill) text-(--platform-text-base) transition hover:border-(--platform-surface-hover-border) hover:text-(--platform-accent)"
onClick={onClose}
>
<X size={16} aria-hidden="true" />
@@ -18,7 +18,7 @@ export function TargetCard({
return (
<button
type="button"
className="group flex aspect-square w-full flex-col items-center justify-center gap-4 rounded-3xl border border-[#e2cbb8] bg-[#fffaf5] p-6 text-center shadow-[0_18px_40px_rgba(112,57,30,0.08)] transition hover:-translate-y-0.5 hover:border-[#c7653d] hover:shadow-[0_22px_48px_rgba(112,57,30,0.14)]"
className="group flex aspect-square w-full flex-col items-center justify-center gap-4 rounded-3xl border border-(--platform-surface-border) bg-(image:--platform-panel-fill) p-6 text-center shadow-(--platform-panel-shadow) transition hover:-translate-y-0.5 hover:border-(--platform-surface-hover-border) hover:shadow-(--platform-profile-action-shadow)"
onClick={() => onOpen(target)}
>
{/* 图标盒恒为正方形:只许按比例缩放,不许拉成矩形。 */}
@@ -29,7 +29,9 @@ export function TargetCard({
className="h-full w-full object-contain transition group-hover:scale-105"
/>
</span>
<span className="text-lg font-semibold text-[#3d1f10]">{meta.label}</span>
<span className="text-lg font-semibold text-(--platform-text-strong)">
{meta.label}
</span>
</button>
);
}
@@ -33,8 +33,8 @@ export function TargetTabs({
aria-current={active ? 'true' : undefined}
className={`relative flex min-w-0 items-center justify-center gap-1.5 px-2 pb-2 pt-1 text-xs font-medium transition-colors after:absolute after:inset-x-3 after:bottom-0 after:h-[2px] after:rounded-full after:transition-colors after:content-[''] ${
active
? 'text-[#c7653d] after:bg-[#c7653d]'
: 'text-[#988476] after:bg-transparent hover:text-[#6f5848]'
? 'text-(--platform-accent) after:bg-(--platform-accent)'
: 'text-(--platform-text-soft) after:bg-transparent hover:text-(--platform-text-base)'
}`}
onClick={() => onSelect(target)}
>
@@ -12,10 +12,13 @@ import type { ReactNode } from 'react';
* 语气 → 类名。用查表而不是嵌套三元:加第四种语气时只加一行,也不用再读一层分支。
*/
const TONE_CLASS: Record<'primary' | 'ghost' | 'brand', string> = {
brand: 'border-[#ff2442] bg-[#ff2442] text-white hover:bg-[#e01e3a]',
primary: 'border-[#c7653d] bg-[#c7653d] text-white hover:bg-[#b4562f]',
// 小红书红是平台自己的品牌色,不在 `--platform-*` 调色板里,按原值保留。
brand:
'border-[#ff2442] bg-[#ff2442] text-(--platform-button-primary-text) hover:bg-[#e01e3a]',
primary:
'border-(--platform-accent) bg-(--platform-accent) text-(--platform-button-primary-text) hover:bg-(--platform-warm-text)',
ghost:
'border-[#e2cbb8] bg-white/80 text-[#6f5848] hover:border-[#c7653d] hover:text-[#c7653d]',
'border-(--platform-surface-border) bg-(--platform-input-fill) text-(--platform-text-base) hover:border-(--platform-surface-hover-border) hover:text-(--platform-accent)',
};
/** 尺寸 → 类名,理由同 [`TONE_CLASS`]。 */
@@ -12,14 +12,14 @@ export function PublishPane({ tab }: { tab: TaonierTabState }) {
const manifest = tab.manifest;
if (!manifest) {
return (
<div className="flex h-full min-h-0 items-center justify-center border-[#e2cbb8] bg-[#fffdf9]/40 p-6 md:border-r">
<p className="text-sm text-[#988476]">正在读取项目清单…</p>
<div className="flex h-full min-h-0 items-center justify-center border-(--platform-surface-border) bg-(--platform-neutral-bg) p-6 md:border-r">
<p className="text-sm text-(--platform-text-soft)">正在读取项目清单…</p>
</div>
);
}
return (
<div
className="flex h-full min-h-0 min-w-0 flex-col border-[#e2cbb8] bg-[#fffdf9]/40 md:border-r"
className="flex h-full min-h-0 min-w-0 flex-col border-(--platform-surface-border) bg-(--platform-neutral-bg) md:border-r"
aria-label="游戏发布表单区"
>
<GameDistributionPublishFormView
@@ -28,12 +28,12 @@ export function AdaptPromptDialog({
onClose={onDoItMyself}
closeOnBackdrop={false}
closeOnEscape={false}
panelClassName="w-full max-w-md rounded-2xl border border-[#e2cbb8] p-4"
panelClassName="w-full max-w-md rounded-2xl border border-(--platform-surface-border) p-4"
>
<h2 className="text-sm font-semibold text-[#3d1f10]">
<h2 className="text-sm font-semibold text-(--platform-text-strong)">
这个项目还没有配置好导出
</h2>
<p className="mt-2 text-xs leading-relaxed text-[#6f5848]">
<p className="mt-2 text-xs leading-relaxed text-(--platform-text-base)">
要让陶泥儿先在项目里帮你调通一次导出流程(用哪套配置、入口在哪、怎么打包都由它试出来)。
调通之后,每次点「打包」就能直接生成代码包。
</p>
@@ -143,27 +143,33 @@ export function ArtifactsPane({
/>
);
} else if (!minitool.readSettled) {
formSlot = <p className="text-xs text-[#988476]">正在读取导出信息…</p>;
formSlot = (
<p className="text-xs text-(--platform-text-soft)">正在读取导出信息…</p>
);
} else if (!failure) {
// 读失败时上面不重复一句话:失败弹窗拿着宿主的现场和下一步。
formSlot = (
<p className="text-xs text-[#988476]">没能读到导出信息,正在自动重试…</p>
<p className="text-xs text-(--platform-text-soft)">
没能读到导出信息,正在自动重试…
</p>
);
}
return (
<div
className="flex h-full min-h-0 min-w-0 flex-col border-[#e2cbb8] bg-[#fffdf9]/40 md:border-r"
className="flex h-full min-h-0 min-w-0 flex-col border-(--platform-surface-border) bg-(--platform-neutral-bg) md:border-r"
aria-label="导出内容区"
>
<div className="flex min-h-0 flex-1 flex-col gap-3 overflow-auto p-3">
{formSlot}
<section
className="rounded-2xl border border-[#e2cbb8] bg-[#fffaf5] p-4"
className="rounded-2xl border border-(--platform-surface-border) bg-(image:--platform-panel-fill) p-4"
aria-label="小工具代码包"
>
<h3 className="text-sm font-bold text-[#3d1f10]">小工具代码包</h3>
<h3 className="text-sm font-bold text-(--platform-text-strong)">
小工具代码包
</h3>
<div className="mt-4 flex flex-col gap-2">
{/*
「打包」不因为有脚本判据没到位就变灰:有没有脚本是**点下去那一刻**才成立的事实
@@ -212,7 +218,7 @@ export function ArtifactsPane({
*/}
<button
type="button"
className="cursor-pointer self-center border-0 bg-transparent p-0 text-xs font-semibold text-[#c7653d] underline underline-offset-2 transition-colors hover:text-[#b4562f] active:text-[#3d1f10] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[#c7653d]"
className="cursor-pointer self-center border-0 bg-transparent p-0 text-xs font-semibold text-(--platform-accent) underline underline-offset-2 transition-colors hover:text-(--platform-warm-text) active:text-(--platform-text-strong) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--platform-accent)"
onClick={() => {
void openUrl(XHS_PUBLISH_URL).catch((error) => {
console.error('[agc] 打开小红书发布页失败', error);
@@ -41,18 +41,18 @@ export function ConflictCard({
<section
role="alertdialog"
aria-label="导出信息冲突"
className="rounded-2xl border border-[#c7653d] bg-[#fff5ef] p-4"
className="rounded-2xl border border-(--platform-accent) bg-(--platform-warm-bg) p-4"
>
<h3 className="text-sm font-semibold text-[#3d1f10]">
<h3 className="text-sm font-semibold text-(--platform-text-strong)">
这份导出信息被改过
</h3>
<p className="mt-1 text-xs text-[#6f5848]">
<p className="mt-1 text-xs text-(--platform-text-base)">
{`下面 ${differing.length} 项两边不一样,各选一个再保存;没列出来的两边一致。`}
</p>
<div className="mt-3 flex flex-col gap-3">
{differing.map((meta) => (
<div key={meta.field} className="flex flex-col gap-1">
<span className="text-xs font-medium text-[#6f5848]">
<span className="text-xs font-medium text-(--platform-text-base)">
{meta.label}
</span>
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
@@ -113,22 +113,26 @@ function Choice({
}) {
return (
<label
className={`flex cursor-pointer flex-col gap-0.5 rounded-lg border bg-white px-2 py-1.5 text-xs transition ${
selected ? 'border-[#c7653d]' : 'border-[#e2cbb8]'
className={`flex cursor-pointer flex-col gap-0.5 rounded-lg border bg-(--platform-input-fill) px-2 py-1.5 text-xs transition ${
selected
? 'border-(--platform-accent)'
: 'border-(--platform-surface-border)'
}`}
>
<span className="flex items-center gap-1 text-[#988476]">
<span className="flex items-center gap-1 text-(--platform-text-soft)">
<input
type="radio"
name={name}
aria-label={`${label}:${caption}`}
checked={selected}
className="accent-[#c7653d]"
className="accent-(--platform-accent)"
onChange={onSelect}
/>
{caption}
</span>
<span className="break-all text-[#3d1f10]">{value || '(空)'}</span>
<span className="break-all text-(--platform-text-strong)">
{value || '(空)'}
</span>
</label>
);
}
@@ -41,7 +41,7 @@ export function CopyButton({ value, label }: { value: string; label: string }) {
className={`inline-flex shrink-0 items-center gap-1 rounded-[10px] border px-2.5 py-1.5 text-xs font-semibold transition disabled:cursor-not-allowed disabled:opacity-45 ${
state === 'failed'
? 'border-red-300 bg-red-50 text-red-600'
: 'border-[#e2cbb8] bg-white/80 text-[#6f5848] hover:border-[#c7653d] hover:text-[#c7653d]'
: 'border-(--platform-surface-border) bg-(--platform-input-fill) text-(--platform-text-base) hover:border-(--platform-surface-hover-border) hover:text-(--platform-accent)'
}`}
onClick={() => {
setState('idle');
@@ -40,23 +40,23 @@ export function DownloadFailureDialog({
onClose={onDismiss}
closeOnBackdrop={false}
closeOnEscape={false}
panelClassName="w-full max-w-md rounded-2xl border border-[#e2cbb8] p-4"
panelClassName="w-full max-w-md rounded-2xl border border-(--platform-surface-border) p-4"
>
<p className="text-sm leading-relaxed text-[#3d1f10]">
<p className="text-sm leading-relaxed text-(--platform-text-strong)">
{failure
? `没能把${failure.kind === 'icon' ? '图标' : '代码包'}保存到你选的位置。`
: null}
</p>
{failure ? (
<p className="mt-2 text-xs leading-relaxed text-[#6f5848]">
<p className="mt-2 text-xs leading-relaxed text-(--platform-text-base)">
{failure.reason}
</p>
) : null}
<p className="mt-2 text-xs leading-relaxed text-[#6f5848]">
<p className="mt-2 text-xs leading-relaxed text-(--platform-text-base)">
也可以让系统在文件管理器里打开它原来的位置,自己复制过去。
</p>
{revealFailure ? (
<p role="status" className="mt-2 text-xs text-[#b4562f]">
<p role="status" className="mt-2 text-xs text-(--platform-warm-text)">
{revealFailure}
</p>
) : null}
@@ -57,9 +57,9 @@ export function FailureDialog({
onClose={onDismiss}
closeOnBackdrop={false}
closeOnEscape={false}
panelClassName="w-full max-w-md rounded-2xl border border-[#e2cbb8] p-4"
panelClassName="w-full max-w-md rounded-2xl border border-(--platform-surface-border) p-4"
>
<p className="text-sm leading-relaxed text-[#3d1f10]">
<p className="text-sm leading-relaxed text-(--platform-text-strong)">
{failure ? describeXhsMinitoolFailure(failure) : null}
</p>
<div className="mt-4 flex flex-wrap justify-end gap-2">
@@ -41,7 +41,7 @@ export function FormCard({
}) {
return (
<section
className="rounded-2xl border border-[#e2cbb8] bg-[#fffaf5] p-4"
className="rounded-2xl border border-(--platform-surface-border) bg-(image:--platform-panel-fill) p-4"
aria-label="上传到小红书时要填的信息"
>
{/*
@@ -50,7 +50,7 @@ export function FormCard({
的自动重读再把值带回来,用户自己打的字先由自动保存落盘。
*/}
<div className="flex items-start justify-between gap-3">
<h3 className="text-sm font-bold text-[#3d1f10]">
<h3 className="text-sm font-bold text-(--platform-text-strong)">
上传到小红书时要填的信息
</h3>
<PaneButton
@@ -101,8 +101,8 @@ export function FormCard({
/** 表单控件的公共皮:与发布面板的输入同宽、同圆角、同暖色焦点环。 */
function fieldInputClass(error: string | null) {
return `box-border w-full rounded-[10px] border bg-white/80 px-3 py-2.5 text-sm leading-[1.45] text-[#3d1f10] outline-none transition placeholder:text-[#b9a494] focus-visible:border-[#c7653d] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[#c7653d]/25 ${
error ? 'border-[#c7653d]' : 'border-[#e2cbb8]'
return `box-border w-full rounded-[10px] border bg-(--platform-input-fill) px-3 py-2.5 text-sm leading-[1.45] text-(--platform-text-strong) outline-none transition placeholder:text-(--platform-text-muted) focus-visible:border-(--platform-accent) focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-(--platform-warm-border) ${
error ? 'border-(--platform-accent)' : 'border-(--platform-surface-border)'
}`;
}
@@ -134,14 +134,19 @@ function FieldRow({
return (
<div className="grid gap-1.5">
<div className="flex items-center justify-between gap-2">
<label htmlFor={id} className="text-xs font-bold text-[#3d1f10]">
<label
htmlFor={id}
className="text-xs font-bold text-(--platform-text-strong)"
>
{label}
</label>
<span className="flex items-center gap-2">
{maxChars !== undefined ? (
<span
className={`text-[11px] font-medium tabular-nums ${
overLimit ? 'text-[#c7653d]' : 'text-[#988476]'
overLimit
? 'text-(--platform-accent)'
: 'text-(--platform-text-soft)'
}`}
>
{used}/{maxChars}
@@ -171,7 +176,10 @@ function FieldRow({
/>
)}
{error ? (
<p role="alert" className="text-xs font-medium text-[#c7653d]">
<p
role="alert"
className="text-xs font-medium text-(--platform-accent)"
>
{error}
</p>
) : null}
@@ -206,13 +214,15 @@ function IconField({
return (
<div className="grid gap-1.5">
<span className="text-xs font-bold text-[#3d1f10]">图标</span>
<span className="text-xs font-bold text-(--platform-text-strong)">
图标
</span>
<div className="flex items-start gap-3">
<IconPreview projectPath={projectPath} relativePath={value} />
<div className="grid min-w-0 flex-1 gap-2">
{/* 没选图标时不摆一行字:缩略图里的占位符已经说了这件事,右侧直接给唯一那颗按钮。 */}
{fileName ? (
<p className="truncate text-sm leading-[1.45] text-[#6f5848]">
<p className="truncate text-sm leading-[1.45] text-(--platform-text-base)">
已选:{fileName}
</p>
) : null}
@@ -241,7 +251,10 @@ function IconField({
</div>
</div>
{error ? (
<p role="alert" className="text-xs font-medium text-[#c7653d]">
<p
role="alert"
className="text-xs font-medium text-(--platform-accent)"
>
{error}
</p>
) : null}
@@ -69,7 +69,7 @@ export function IconPreview({
return (
<span
className="grid size-24 shrink-0 place-items-center overflow-hidden rounded-xl border border-[#e2cbb8] bg-white/80"
className="grid size-24 shrink-0 place-items-center overflow-hidden rounded-xl border border-(--platform-surface-border) bg-(--platform-input-fill)"
title={
failed ? '读不到这张图,用「选择图标」导入一张就能预览' : undefined
}
@@ -85,7 +85,9 @@ export function IconPreview({
) : (
<ImageOff
size={18}
className={failed ? 'text-[#c7653d]' : 'text-[#b9a494]'}
className={
failed ? 'text-(--platform-accent)' : 'text-(--platform-text-muted)'
}
aria-hidden="true"
/>
)}