Compare commits

...
2 Commits
19 changed files with 327 additions and 126 deletions
@@ -167,28 +167,34 @@
<div class="stage-media" in:xfade out:xfade>
{#if output && video}
<!-- svelte-ignore a11y_media_has_caption -->
<video
bind:this={videoEl}
src={assetUrl(output)}
controls
playsinline
preload="metadata"
onloadedmetadata={(e) => {
clearOutputMissing(output);
noteVideo(e);
}}
onerror={() => output && markOutputMissing(output)}
></video>
<div class="print a-photo">
{#if st.tone === 'ok' && !fileGone}<span class="a-fix">{t('jobst.done')}</span>{/if}
<video
bind:this={videoEl}
src={assetUrl(output)}
controls
playsinline
preload="metadata"
onloadedmetadata={(e) => {
clearOutputMissing(output);
noteVideo(e);
}}
onerror={() => output && markOutputMissing(output)}
></video>
</div>
{:else if output}
<img
src={assetUrl(output)}
alt={prompt || t('desk.tabResult')}
onload={(e) => {
clearOutputMissing(output);
noteImg(e);
}}
onerror={() => output && markOutputMissing(output)}
/>
<div class="print a-photo">
{#if st.tone === 'ok' && !fileGone}<span class="a-fix">{t('jobst.done')}</span>{/if}
<img
src={assetUrl(output)}
alt={prompt || t('desk.tabResult')}
onload={(e) => {
clearOutputMissing(output);
noteImg(e);
}}
onerror={() => output && markOutputMissing(output)}
/>
</div>
{:else if fileGone}
<div class="stage-empty gone">
<span>{t('desk.missingTitle')}</span>
@@ -362,14 +368,24 @@
inset: 0;
display: grid;
place-items: center;
padding: var(--sp-5);
box-sizing: border-box;
}
.stage-media > img,
.stage-media > video {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: contain;
/* 相纸白边 (HEL-680):检视舞台上的结果按相纸呈现,媒体自适应内含 */
.print {
position: relative;
display: grid;
max-width: 100%;
max-height: 100%;
--paper-w: 5px;
}
.print > img,
.print > video {
display: block;
max-width: 100%;
max-height: 100%;
width: auto;
height: auto;
}
.stage-empty {
display: grid;
@@ -183,7 +183,8 @@
<div class="out">
{#if output}
<div class="thumb" class:portrait>
<div class="thumb a-photo" class:portrait>
{#if st.tone === 'ok' && !fileGone}<span class="a-fix">{t('jobst.done')}</span>{/if}
{#if isVideoOutput(job, output)}
<video
src={assetUrl(output)}
@@ -238,15 +239,42 @@
background-color var(--d-state) ease,
box-shadow var(--d-state) ease;
}
/* 选中框:金色描边从上一条记录滑到新选中的记录(feed 驱动 WAAPI) */
/* 选中框:取景框角线从上一条记录滑到新选中的记录(feed 驱动 WAAPI,
HEL-680 四角短线取代整框描边) */
.sel-mark {
position: absolute;
inset: -1px;
z-index: 3;
display: none;
border: 1px solid var(--accent-line);
border-radius: inherit;
pointer-events: none;
background-image:
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0);
background-position:
left top,
left top,
right top,
right top,
left bottom,
left bottom,
right bottom,
right bottom;
background-size:
14px 2px,
2px 14px,
14px 2px,
2px 14px,
14px 2px,
2px 14px,
14px 2px,
2px 14px;
background-repeat: no-repeat;
}
.take.selected {
border-color: transparent;
@@ -458,6 +486,12 @@
background: var(--bg-4);
box-shadow: inset 0 0 0 1px var(--on-media-line);
}
/* 相纸白边 (HEL-680):有产出的记录缩略图按相纸呈现,压过占位态样式 */
.thumb.a-photo {
border-radius: 2px;
background: var(--accent);
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
}
.thumb.portrait {
width: 96px;
aspect-ratio: 3 / 4;
@@ -52,7 +52,12 @@
'var(--avatar-4)',
'var(--avatar-5)',
];
const placeholder = $derived(PLACEHOLDER_GRADIENTS[project.id % PLACEHOLDER_GRADIENTS.length]);
const placeholderIdx = $derived(project.id % PLACEHOLDER_GRADIENTS.length);
const placeholder = $derived(PLACEHOLDER_GRADIENTS[placeholderIdx]);
// HEL-680 灰阶封面占位:1–2 亮阶配深图标,3–5 暗阶配浅图标
const placeholderIconColor = $derived(
placeholderIdx <= 1 ? 'var(--accent-ink)' : 'var(--text)',
);
const coverUrl = $derived(assetUrl(project.cover_path));
@@ -174,7 +179,12 @@
onerror={() => (coverFailed = true)}
/>
{:else}
<span class="cover-ph" style:background={placeholder} aria-hidden="true">
<span
class="cover-ph"
style:background={placeholder}
style:color={placeholderIconColor}
aria-hidden="true"
>
<Icon name="film" size={22} />
</span>
{/if}
@@ -402,6 +402,7 @@
flex-shrink: 0;
margin-bottom: var(--sp-5);
border-radius: var(--r-md);
border-bottom: 2px solid var(--accent-lo);
display: grid;
place-items: center;
background: var(--accent-grad);
@@ -132,9 +132,7 @@ let {
min-height: 0;
width: 100%;
height: 100%;
background:
radial-gradient(ellipse at center, rgba(139, 92, 246, 0.08), transparent 55%),
#050508;
background: var(--stage-bg);
border: 1px solid var(--border);
border-radius: var(--radius-md);
overflow: hidden;
@@ -79,14 +79,14 @@
let criticNotes = $state<string[]>([]);
const preview = createMutation({
mutationFn: async (vars?: { force?: boolean }) => {
mutationFn: async (vars: { force?: boolean } | void) => {
if (!clip && !scene) throw new Error(t('promptPreview.noClipSelected'));
return jobsApi.previewPrompt(projectId, {
clip_id: clip?.id,
scene_id: clip ? undefined : scene?.id,
workflow_id: workflow?.id,
input_values: compactInputValues(inputValues),
force: Boolean(vars?.force),
force: Boolean((vars as { force?: boolean } | undefined)?.force),
});
},
onSuccess: (data) => {
@@ -203,7 +203,7 @@ title={t('filmstrip.clipTitle', {
inset: 0 0 auto 0;
height: 3px;
z-index: 1;
background: #3f3f46;
background: var(--bg-4);
}
.clip.status-done .bar {
+1 -1
View File
@@ -1902,7 +1902,7 @@ export const de: Dict = {
'home.stageProgress': 'Fünf-Phasen-Fortschritt',
'tasks.sourceWorkbench': 'Kreativ-Fluss',
'jobst.idSuffix': ' · ComfyUI-Prompt-ID {id}',
'jobst.done': 'Fertig',
'jobst.done': 'Fixiert',
'jobst.doneDetail': 'ComfyUI ist fertig, Ergebnisse gespeichert',
'jobst.dryRun': 'Platzhalter',
'jobst.dryRunDetail': 'Nur Platzhalter: Dry-run ist aktiv, ComfyUI wurde nicht aufgerufen',
+1 -1
View File
@@ -1901,7 +1901,7 @@ export const en = {
'home.stageProgress': 'Five-stage progress',
'tasks.sourceWorkbench': 'Creation flow',
'jobst.idSuffix': ' · ComfyUI prompt ID {id}',
'jobst.done': 'Done',
'jobst.done': 'Fixed',
'jobst.doneDetail': 'ComfyUI finished and the results are saved',
'jobst.dryRun': 'Placeholder',
'jobst.dryRunDetail': 'Placeholder only: Dry-run is on, ComfyUI was not called',
+1 -1
View File
@@ -1902,7 +1902,7 @@ export const es: Dict = {
'home.stageProgress': 'Progreso de cinco etapas',
'tasks.sourceWorkbench': 'Flujo de creación',
'jobst.idSuffix': ' · ID de prompt de ComfyUI {id}',
'jobst.done': 'Listo',
'jobst.done': 'Fijado',
'jobst.doneDetail': 'ComfyUI terminó y los resultados se guardaron',
'jobst.dryRun': 'Marcador',
'jobst.dryRunDetail': 'Solo marcador: Dry-run activo, no se llamó a ComfyUI',
+1 -1
View File
@@ -1902,7 +1902,7 @@ export const fr: Dict = {
'home.stageProgress': 'Progression en cinq étapes',
'tasks.sourceWorkbench': 'Flux de création',
'jobst.idSuffix': ' · ID de prompt ComfyUI {id}',
'jobst.done': 'Terminé',
'jobst.done': 'Fixé',
'jobst.doneDetail': 'ComfyUI a terminé, résultats enregistrés',
'jobst.dryRun': 'Substitut',
'jobst.dryRunDetail': 'Substitut uniquement : Dry-run actif, ComfyUI n\'a pas été appelé',
+1 -1
View File
@@ -1900,7 +1900,7 @@ export const ja: Dict = {
'home.stageProgress': '五段階の進捗',
'tasks.sourceWorkbench': '制作フロー',
'jobst.idSuffix': ' · ComfyUI プロンプト ID {id}',
'jobst.done': '完了',
'jobst.done': '定着',
'jobst.doneDetail': 'ComfyUI の実行が完了し、結果を保存しました',
'jobst.dryRun': 'プレースホルダー',
'jobst.dryRunDetail': 'プレースホルダーのみ:Dry-run が有効で ComfyUI は呼び出されていません',
+1 -1
View File
@@ -1900,7 +1900,7 @@ export const ko: Dict = {
'home.stageProgress': '5단계 진행',
'tasks.sourceWorkbench': '제작 흐름',
'jobst.idSuffix': ' · ComfyUI 프롬프트 ID {id}',
'jobst.done': '완료',
'jobst.done': '정착',
'jobst.doneDetail': 'ComfyUI 실행이 끝나 결과를 저장했습니다',
'jobst.dryRun': '자리표시자',
'jobst.dryRunDetail': '자리표시자만: Dry-run이 켜져 있어 ComfyUI를 호출하지 않았습니다',
+2 -2
View File
@@ -1900,8 +1900,8 @@ export const zh: Dict = {
'home.stageProgress': '五段进度',
'tasks.sourceWorkbench': '创作流程',
'jobst.idSuffix': ' · ComfyUI 任务 ID {id}',
'jobst.done': '完成',
'jobst.doneDetail': 'ComfyUI 已执行完成,结果已保存',
'jobst.done': '定影',
'jobst.doneDetail': '生成完成,结果已定影保存',
'jobst.dryRun': '占位结果',
'jobst.dryRunDetail': '占位结果:当前开启了 Dry-run,没有调用 ComfyUI,不是真实生成',
'jobst.noOutput': '无产出',
+7 -2
View File
@@ -589,6 +589,11 @@
function avatarStyle(n: number): string {
return `var(--avatar-${Math.min(Math.max(n, 1), 5)})`;
}
// HEL-680 灰阶头像:1–2 亮阶配深字,3–5 暗阶配浅字,按阶数决定。
function avatarLight(n: number | undefined): boolean {
return Math.min(Math.max(n ?? 1, 1), 5) <= 2;
}
</script>
{#snippet skillRow(skill: SkillAdminEntry, detail: string)}
@@ -655,7 +660,7 @@
class:selected
onclick={() => selectAgent(agent.key)}
>
<span class="avatar" class:dark={agent.avatar_dark} style:background={avatarStyle(agent.avatar)}>
<span class="avatar" class:dark={avatarLight(agent.avatar)} style:background={avatarStyle(agent.avatar)}>
{agent.char}
</span>
<span class="agent-meta">
@@ -693,7 +698,7 @@
<section class="mid">
{#if activeAgent}
<header class="chat-head">
<span class="avatar lg" class:dark={activeAgent.avatar_dark} style:background={avatarStyle(activeAgent.avatar)}>
<span class="avatar lg" class:dark={avatarLight(activeAgent.avatar)} style:background={avatarStyle(activeAgent.avatar)}>
{activeAgent.char}
</span>
<div class="chat-head-meta">
+181 -59
View File
@@ -4,62 +4,67 @@
:root {
/* ------------------------------------------------------------------
* 融合视觉 design tokens — HEL-649 整站施工规范 §一(流水台结构 ×
* 放映厅配色)。Single source of truth for the whole app: pages and
* components reference these variables only — no hardcoded colors,
* no ad-hoc overrides. Gold is for key actions and the current
* selection only; status colors each carry exactly one meaning.
* 无彩灰阶 design tokens — HEL-680 S1(白栖知 theme-1「暗房放大机」
* × HEL-679 已确认融合样稿)。Single source of truth for the whole
* app: pages and components reference these variables only — no
* hardcoded colors, no ad-hoc overrides. 界面只有暖调黑白灰,作品是
* 屏幕上唯一成片的颜色;状态色仅按主题卡的两处有限例外(琥珀
* #A3915F、红 #B3564A)使用,不放大成全站主色。选中=取景框角线,
* 完成=「定影」,结果=相纸白边。
* ------------------------------------------------------------------ */
/* 底色:蓝黑四阶,逐层浮起 (§1.1) */
--bg: #07090e;
--bg-2: #0c0f16;
--bg-3: #121724;
--bg-4: #1a2133;
--line: #1b2233;
--line-2: #2a3450;
--page-glow: radial-gradient(1200px 500px at 70% -10%, rgba(38, 52, 92, 0.25), transparent 60%);
--float-bg: linear-gradient(180deg, #10152a, var(--bg-2));
--overlay: rgba(4, 6, 10, 0.6);
--media-scrim: rgba(7, 9, 14, 0.65);
/* 底色:显影灰阶四阶,逐层浮起(theme-1:#212120 底 / #2C2C2A 面板) */
--bg: #212120;
--bg-2: #262625;
--bg-3: #2c2c2a;
--bg-4: #383835;
--line: #313130;
--line-2: #454542;
--page-glow: radial-gradient(1200px 500px at 70% -10%, rgba(245, 244, 238, 0.035), transparent 60%);
--float-bg: linear-gradient(180deg, #2e2e2c, var(--bg-2));
--overlay: rgba(18, 18, 17, 0.62);
--media-scrim: rgba(18, 18, 17, 0.66);
--on-media: #fff;
--on-media-line: rgba(255, 255, 255, 0.05);
--composer-bg: linear-gradient(180deg, #0e1220, var(--bg-2));
--composer-focus: 0 0 0 1px rgba(242, 199, 132, 0.12), 0 8px 28px rgba(0, 0, 0, 0.4);
--composer-bg: linear-gradient(180deg, #2a2a28, var(--bg-2));
--composer-focus: 0 0 0 1px rgba(245, 244, 238, 0.12), 0 8px 28px rgba(0, 0, 0, 0.4);
/* 放映金:主按钮、当前选中、改过的参数、聚焦框、Toast 动作 (§1.2) */
--accent: #f2c784;
--accent-ink: #23170a;
--accent-soft: rgba(242, 199, 132, 0.13);
--accent-hi: #f7d9a2;
--accent-lo: #ecaf63;
--accent-glow: rgba(236, 175, 99, 0.28);
--accent-line: rgba(242, 199, 132, 0.55);
/* 相纸白(重点色):主按钮、当前选中、改过的参数、聚焦框、Toast 动作
(theme-1 #F5F4EE 重点) */
--accent: #f5f4ee;
--accent-ink: #212120;
--accent-soft: rgba(245, 244, 238, 0.08);
--accent-hi: #ffffff;
--accent-lo: #ddddd7;
--accent-glow: rgba(240, 239, 233, 0.2);
--accent-line: rgba(245, 244, 238, 0.55);
--accent-grad: linear-gradient(180deg, var(--accent-hi), var(--accent-lo));
--accent-grad-hover: linear-gradient(180deg, #fbe3b6, #f0b968);
--accent-ring: 0 0 0 1px rgba(242, 199, 132, 0.7), 0 0 22px var(--accent-glow);
--btn-glow: 0 2px 14px var(--accent-glow), inset 0 1px 0 rgba(255, 255, 255, 0.35);
--btn-glow-hover: 0 4px 18px var(--accent-glow), inset 0 1px 0 rgba(255, 255, 255, 0.4);
--accent-wash: linear-gradient(180deg, rgba(242, 199, 132, 0.05), transparent 40%);
--sel-shadow: 0 0 0 1px rgba(242, 199, 132, 0.15), 0 6px 24px rgba(0, 0, 0, 0.35);
--accent-grad-hover: linear-gradient(180deg, #ffffff, #e8e7df);
--accent-ring: 0 0 0 1px rgba(245, 244, 238, 0.7), 0 0 22px var(--accent-glow);
--btn-glow: 0 2px 10px rgba(0, 0, 0, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.55);
--btn-glow-hover: 0 3px 14px rgba(0, 0, 0, 0.44), inset 0 1px 0 rgba(255, 255, 255, 0.65);
--accent-wash: linear-gradient(180deg, rgba(245, 244, 238, 0.05), transparent 40%);
--sel-shadow: 0 0 0 1px rgba(245, 244, 238, 0.14), 0 6px 24px rgba(0, 0, 0, 0.35);
/* 信号色:绿=完成/已确认/通过,青=进行中/在线,红=失败/危险,
琥珀=待确认/文件缺失/限额 (§1.3) */
--ok: #49cb90;
--ok-soft: rgba(73, 203, 144, 0.13);
--run: #54dfd2;
--run-soft: rgba(84, 223, 210, 0.13);
--run-grad: linear-gradient(90deg, #3bbfb4, var(--run));
--err: #f0645a;
--err-soft: rgba(240, 100, 90, 0.13);
--err-line: rgba(240, 100, 90, 0.6);
--warn: #f0b45c;
--warn-soft: rgba(240, 180, 92, 0.13);
/* 信号色:完成=显影亮灰(「定影」,配图标/文字双编码),进行中=灰阶
+动效;红/琥珀只按 theme-1 卡片的有限例外使用(红=失败/危险
#B3564A 系,琥珀=待确认/文件缺失 #A3915F) */
--ok: #ddddd7;
--ok-soft: rgba(221, 221, 215, 0.1);
--run: #b8b6ac;
--run-soft: rgba(184, 182, 172, 0.12);
--run-grad: linear-gradient(90deg, #8f8e85, #c6c5c0);
--err: #d07a6b;
--err-solid: #b3564a;
--err-soft: rgba(181, 86, 74, 0.15);
--err-line: rgba(208, 122, 107, 0.55);
--warn: #a3915f;
--warn-soft: rgba(163, 145, 95, 0.13);
/* 文字与字体 (§1.4);--text-2 是深底可读性下限,不得再暗 */
--text: #f4f6fa;
--text-2: #aab2c5;
--text-3: #6d7690;
/* 文字与字体;--text-3 是深底可读性下限(≥4.5:1 on --bg-3),不得再暗 */
--text: #f0efe9;
--text-2: #a8a69c;
--text-3: #94938c;
--font: 'PingFang SC', 'Noto Sans SC', 'Microsoft YaHei', system-ui, sans-serif;
--mono: 'JetBrains Mono', ui-monospace, monospace;
--fs-title: 15px;
@@ -95,8 +100,14 @@
/* 专属符号 (§1.7):齿孔只在记录流/输入台分隔带,光晕只在播放舞台 */
--sprocket: radial-gradient(circle 2px, var(--line-2) 98%, transparent);
--stage-bg:
radial-gradient(60% 45% at 50% 38%, rgba(242, 199, 132, 0.07), transparent 70%),
radial-gradient(80% 70% at 50% 50%, #0d1220, #05070c);
radial-gradient(60% 45% at 50% 38%, rgba(245, 244, 238, 0.045), transparent 70%),
radial-gradient(80% 70% at 50% 50%, #232322, #171716);
/* 取景框角线与相纸白边 (HEL-680):内容级选中/结果呈现的统一语言 */
--frame-w: 2px;
--frame-arm: 12px;
--frame-inset: -4px;
--paper-w: 4px;
/* 动效 (§1.6,取 HEL-648 实测值);只动 transform / opacity */
--ease-out: cubic-bezier(0.23, 1, 0.32, 1);
@@ -113,13 +124,13 @@
--d-progress: 400ms;
--press-scale: 0.97;
/* Avatar five-color set — five agents only; the one colorful gradient
family the spec keeps (§3.6). */
--avatar-1: linear-gradient(135deg, #e8a33d, #b96a1b);
--avatar-2: linear-gradient(135deg, #5b8def, #3a5fa9);
--avatar-3: linear-gradient(135deg, #3fb97f, #2a7a54);
--avatar-4: linear-gradient(135deg, #b98be0, #7a4fa9);
--avatar-5: linear-gradient(135deg, #d98e6a, #a95535);
/* Avatar five-step grayscale (HEL-680 无彩化):1–2 亮阶配深字,
3–5 暗阶配浅字;仍是五个身份位,只是从彩色渐变改为显影灰阶。 */
--avatar-1: linear-gradient(135deg, #e8e7df, #cdcbc4);
--avatar-2: linear-gradient(135deg, #b5b3aa, #9c9a91);
--avatar-3: linear-gradient(135deg, #6b6a66, #55544f);
--avatar-4: linear-gradient(135deg, #55544f, #424140);
--avatar-5: linear-gradient(135deg, #424140, #2e2e2c);
/* ------------------------------------------------------------------
* Legacy token names (§六 映射表) — aliases only, never new values,
@@ -297,7 +308,7 @@ code,
color: var(--err);
}
.a-btn.danger-solid {
background: var(--err);
background: var(--err-solid);
color: var(--on-media);
}
@media (hover: hover) and (pointer: fine) {
@@ -550,11 +561,47 @@ button.status-chip:active {
.a-chip.changed {
color: var(--accent);
}
/* Selected filter chip (§3.5 筛选芯片组): 当前选中 = 金色文字 + accent-soft 底 */
/* Selected filter chip (§3.5 筛选芯片组): 当前选中 = 取景框角线缩影 +
相纸白文字 + accent-soft 底 (HEL-680) */
.a-chip.sel {
position: relative;
background: var(--accent-soft);
color: var(--accent);
}
.a-chip.sel::after {
content: '';
position: absolute;
inset: 2px;
pointer-events: none;
background-image:
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0);
background-position:
left top,
left top,
right top,
right top,
left bottom,
left bottom,
right bottom,
right bottom;
background-size:
6px 1.5px,
1.5px 6px,
6px 1.5px,
1.5px 6px,
6px 1.5px,
1.5px 6px,
6px 1.5px,
1.5px 6px;
background-repeat: no-repeat;
}
.a-chip.warn {
color: var(--warn);
}
@@ -753,6 +800,81 @@ button.a-tile:disabled {
box-shadow: 0 0 0 1px var(--line);
}
/* ------------------------------------------------------------------
* 取景框角线选中 (HEL-680):内容级选中态的唯一语言——四角短线取景,
* 不整框描边。挂在需要角线的元素上(元素自身可 overflow 可见;被
* overflow:hidden 裁切的容器改用 --frame-inset 正值画在内侧)。
* ------------------------------------------------------------------ */
.a-frame {
position: relative;
}
.a-frame::after {
content: '';
position: absolute;
inset: var(--frame-inset, -4px);
z-index: 3;
pointer-events: none;
background-image:
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0),
linear-gradient(var(--accent) 0 0);
background-position:
left top,
left top,
right top,
right top,
left bottom,
left bottom,
right bottom,
right bottom;
background-size:
var(--frame-arm, 12px) var(--frame-w, 2px),
var(--frame-w, 2px) var(--frame-arm, 12px),
var(--frame-arm, 12px) var(--frame-w, 2px),
var(--frame-w, 2px) var(--frame-arm, 12px),
var(--frame-arm, 12px) var(--frame-w, 2px),
var(--frame-w, 2px) var(--frame-arm, 12px),
var(--frame-arm, 12px) var(--frame-w, 2px),
var(--frame-w, 2px) var(--frame-arm, 12px);
background-repeat: no-repeat;
}
/* 相纸白边 (HEL-680):结果媒体外的相纸白框,底边略厚如手工相纸。
挂在承载 img/video 的媒体容器上;容器尺寸由调用方决定。 */
.a-photo {
background: var(--accent);
border: var(--paper-w, 4px) solid var(--accent);
border-bottom-width: calc(var(--paper-w, 4px) + 2px);
border-radius: 2px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.35);
box-sizing: border-box;
}
/* 「定影」完成章 (HEL-680):已定影结果右上角的完成印记,随 jobst.done
文案走(中文=定影)。absolute 定位,挂在有媒体的结果容器内。 */
.a-fix {
position: absolute;
top: var(--sp-3);
right: var(--sp-3);
z-index: 2;
padding: 1px 6px;
border: 1px solid currentColor;
border-radius: 3px;
background: var(--media-scrim);
color: var(--ok);
font-size: var(--fs-mini);
font-weight: 700;
letter-spacing: 0.1em;
text-indent: 0.1em;
transform: rotate(-4deg);
pointer-events: none;
}
/* Floating surface for pop / drawer / confirm / toast / lightbox (§四.3). */
.a-float {
background: var(--float-bg);
+21 -8
View File
@@ -209,7 +209,13 @@
{:else}
<div class="grid">
{#each visible as item (item.path)}
<div class="card" class:selected={selected.has(item.path)}>
<div
class="card"
class:selected={selected.has(item.path)}
class:a-frame={selected.has(item.path)}
style:--frame-inset="3px"
style:--frame-arm="16px"
>
<label class="check">
<input
type="checkbox"
@@ -217,12 +223,12 @@
onchange={() => toggle(item.path)}
/>
</label>
<button
type="button"
class="media"
onclick={() => openPreview(item)}
title={item.name}
>
<button
type="button"
class="media a-photo"
onclick={() => openPreview(item)}
title={item.name}
>
<SafeMedia src={assetUrl(item.path)} kind={item.kind} alt={item.name} controls={false} />
</button>
<div class="meta">
@@ -341,7 +347,6 @@
transition: border-color var(--d-state) ease;
}
.card.selected {
border-color: var(--accent-line);
background: linear-gradient(180deg, var(--accent-soft), transparent 32%), var(--bg-2);
}
.card :global(.media-el) {
@@ -380,6 +385,14 @@
cursor: zoom-in;
overflow: hidden;
}
/* 相纸白边 (HEL-680):素材墙里的作品按相纸呈现 */
.media.a-photo {
border: var(--paper-w, 4px) solid var(--accent);
border-bottom-width: calc(var(--paper-w, 4px) + 2px);
background: var(--accent);
border-radius: 0;
box-sizing: border-box;
}
.media :global(img),
.media :global(video) {
width: 100%;
+2 -8
View File
@@ -392,14 +392,8 @@
char: string;
dark?: boolean;
}[] = [
{
role: 'story',
labelKey: 'models.agentStory',
color: 'var(--avatar-1)',
char: '剧',
dark: true,
},
{ role: 'style', labelKey: 'models.agentStyle', color: 'var(--avatar-2)', char: '视' },
{ role: 'story', labelKey: 'models.agentStory', color: 'var(--avatar-1)', char: '剧', dark: true },
{ role: 'style', labelKey: 'models.agentStyle', color: 'var(--avatar-2)', char: '视', dark: true },
{ role: 'script', labelKey: 'models.agentScript', color: 'var(--avatar-3)', char: '分' },
{ role: 'assets', labelKey: 'models.agentAssets', color: 'var(--avatar-4)', char: '素' },
{ role: 'video', labelKey: 'models.agentVideo', color: 'var(--avatar-5)', char: '镜' },
@@ -496,7 +496,11 @@
<aside class="side-col">
{#if currentAgent}
<div class="side-card agent-card">
<span class="agent-avatar" style:background={`var(--avatar-${currentAgent.avatar})`}>
<span
class="agent-avatar"
class:light={(currentAgent.avatar ?? 1) <= 2}
style:background={`var(--avatar-${currentAgent.avatar})`}
>
{currentAgent.char}
</span>
<div class="agent-copy">
@@ -740,6 +744,10 @@
color: var(--text);
flex-shrink: 0;
}
/* HEL-680 灰阶头像:1–2 亮阶用深字 */
.agent-avatar.light {
color: var(--accent-ink);
}
.agent-copy {
display: flex;
flex-direction: column;