Skip to content

Commit 4aaf7f8

Browse files
committed
update
1 parent d0d023e commit 4aaf7f8

3 files changed

Lines changed: 145 additions & 22 deletions

File tree

src/App.tsx

Lines changed: 126 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ const DEFAULT_VIEWPORT: Viewport = { x: -22, y: 42, zoom: 1.24 };
202202
const GRAPH_FONT_SCALE_STORAGE_KEY = 'acn-ui-graph-font-scale';
203203
const CANVAS_VIEWPORT_STORAGE_KEY = 'acn-ui-canvas-viewport';
204204
const SIDEBAR_WIDTH_STORAGE_KEY = 'acn-ui-sidebar-width';
205+
const CAPTION_CARD_SCALE_STORAGE_KEY = 'acn-ui-caption-card-scale';
206+
const CAPTION_OFFSET_STORAGE_KEY = 'acn-ui-caption-offset';
205207
const CONTINUOUS_PLAY_STORAGE_KEY = 'acn-ui-continuous-play';
206208
const PLAY_SPEED_STORAGE_KEY = 'acn-ui-play-speed';
207209
const DEFAULT_SIDEBAR_WIDTH = 487;
@@ -210,6 +212,10 @@ const MAX_SIDEBAR_WIDTH = 560;
210212
const DEFAULT_GRAPH_FONT_SCALE = 1.2;
211213
const MIN_GRAPH_FONT_SCALE = 0.9;
212214
const MAX_GRAPH_FONT_SCALE = 1.6;
215+
const DEFAULT_CAPTION_CARD_SCALE = 1;
216+
const MIN_CAPTION_CARD_SCALE = 0.85;
217+
const MAX_CAPTION_CARD_SCALE = 1.35;
218+
const DEFAULT_CAPTION_OFFSET = { x: 0, y: 0 };
213219
const GRAPH_ZOOM_STEP = 1.03;
214220
const CONTINUOUS_PLAY_GATE_DELAY_MS = 1000;
215221
const DEFAULT_PLAY_SPEED = 1;
@@ -273,6 +279,7 @@ const CATALOG_KEY_ALIASES: Record<string, string> = {
273279
showScript: 'ui.settings.showScript',
274280
hideScript: 'ui.settings.hideScript',
275281
graphTextSize: 'ui.settings.graphTextSize',
282+
captionCardSize: 'ui.settings.captionCardSize',
276283
graphTextScaleValue: 'ui.settings.graphTextScaleValue',
277284
playSpeed: 'ui.settings.playSpeed',
278285
playSpeedValue: 'ui.settings.playSpeedValue',
@@ -381,6 +388,36 @@ function getInitialCanvasViewport(): Viewport {
381388
}
382389
}
383390

391+
function getInitialCaptionCardScale() {
392+
if (typeof window === 'undefined') {
393+
return DEFAULT_CAPTION_CARD_SCALE;
394+
}
395+
const raw = Number.parseFloat(window.localStorage.getItem(CAPTION_CARD_SCALE_STORAGE_KEY) ?? '');
396+
if (!Number.isFinite(raw)) {
397+
return DEFAULT_CAPTION_CARD_SCALE;
398+
}
399+
return Math.min(MAX_CAPTION_CARD_SCALE, Math.max(MIN_CAPTION_CARD_SCALE, raw));
400+
}
401+
402+
function getInitialCaptionOffset() {
403+
if (typeof window === 'undefined') {
404+
return DEFAULT_CAPTION_OFFSET;
405+
}
406+
try {
407+
const raw = window.localStorage.getItem(CAPTION_OFFSET_STORAGE_KEY);
408+
if (!raw) {
409+
return DEFAULT_CAPTION_OFFSET;
410+
}
411+
const parsed = JSON.parse(raw) as Partial<typeof DEFAULT_CAPTION_OFFSET>;
412+
if (typeof parsed.x !== 'number' || typeof parsed.y !== 'number') {
413+
return DEFAULT_CAPTION_OFFSET;
414+
}
415+
return { x: parsed.x, y: parsed.y };
416+
} catch {
417+
return DEFAULT_CAPTION_OFFSET;
418+
}
419+
}
420+
384421
function getInitialSidebarWidth() {
385422
if (typeof window === 'undefined') {
386423
return DEFAULT_SIDEBAR_WIDTH;
@@ -2325,6 +2362,8 @@ function Dashboard() {
23252362
const [lastBackendPollAt, setLastBackendPollAt] = useState<number | null>(null);
23262363
const [canvasViewport, setCanvasViewport] = useState<Viewport>(() => getInitialCanvasViewport());
23272364
const [graphFontScale, setGraphFontScale] = useState(() => getInitialGraphFontScale());
2365+
const [captionCardScale, setCaptionCardScale] = useState(() => getInitialCaptionCardScale());
2366+
const [captionOffset, setCaptionOffset] = useState(() => getInitialCaptionOffset());
23282367
const [playSpeed, setPlaySpeed] = useState(() => getInitialPlaySpeed());
23292368
const [continuousPlay, setContinuousPlay] = useState(() => getInitialContinuousPlay());
23302369
const [backendEnabled, setBackendEnabled] = useState(() => !initialDebugMode);
@@ -2357,6 +2396,8 @@ function Dashboard() {
23572396
const playtimeAccumulatedRef = useRef(0);
23582397
const reactFlowRef = useRef<ReactFlowInstance<Node, Edge> | null>(null);
23592398
const sidebarResizeStartRef = useRef<{ pointerX: number; width: number } | null>(null);
2399+
const captionDragStartRef = useRef<{ pointerX: number; pointerY: number; x: number; y: number } | null>(null);
2400+
const [captionDragging, setCaptionDragging] = useState(false);
23602401

23612402
useEffect(() => {
23622403
scriptRef.current = scriptDoc;
@@ -2419,6 +2460,18 @@ function Dashboard() {
24192460
}
24202461
}, [graphFontScale]);
24212462

2463+
useEffect(() => {
2464+
if (typeof window !== 'undefined') {
2465+
window.localStorage.setItem(CAPTION_CARD_SCALE_STORAGE_KEY, String(captionCardScale));
2466+
}
2467+
}, [captionCardScale]);
2468+
2469+
useEffect(() => {
2470+
if (typeof window !== 'undefined') {
2471+
window.localStorage.setItem(CAPTION_OFFSET_STORAGE_KEY, JSON.stringify(captionOffset));
2472+
}
2473+
}, [captionOffset]);
2474+
24222475
useEffect(() => {
24232476
if (typeof window !== 'undefined') {
24242477
window.localStorage.setItem(PLAY_SPEED_STORAGE_KEY, String(playSpeed));
@@ -2446,20 +2499,36 @@ function Dashboard() {
24462499
useEffect(() => {
24472500
const handlePointerMove = (event: PointerEvent) => {
24482501
const start = sidebarResizeStartRef.current;
2449-
if (!start) {
2502+
if (start) {
2503+
const nextWidth = start.width + (start.pointerX - event.clientX);
2504+
setSidebarWidth(Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, nextWidth)));
2505+
return;
2506+
}
2507+
const captionStart = captionDragStartRef.current;
2508+
if (!captionStart) {
24502509
return;
24512510
}
2452-
const nextWidth = start.width + (start.pointerX - event.clientX);
2453-
setSidebarWidth(Math.min(MAX_SIDEBAR_WIDTH, Math.max(MIN_SIDEBAR_WIDTH, nextWidth)));
2511+
setCaptionOffset({
2512+
x: captionStart.x + (event.clientX - captionStart.pointerX),
2513+
y: captionStart.y + (event.clientY - captionStart.pointerY),
2514+
});
24542515
};
24552516

24562517
const handlePointerUp = () => {
2457-
if (!sidebarResizeStartRef.current) {
2458-
return;
2518+
let released = false;
2519+
if (sidebarResizeStartRef.current) {
2520+
sidebarResizeStartRef.current = null;
2521+
released = true;
2522+
}
2523+
if (captionDragStartRef.current) {
2524+
captionDragStartRef.current = null;
2525+
setCaptionDragging(false);
2526+
released = true;
2527+
}
2528+
if (released) {
2529+
document.body.style.cursor = '';
2530+
document.body.style.userSelect = '';
24592531
}
2460-
sidebarResizeStartRef.current = null;
2461-
document.body.style.cursor = '';
2462-
document.body.style.userSelect = '';
24632532
};
24642533

24652534
window.addEventListener('pointermove', handlePointerMove);
@@ -2651,17 +2720,36 @@ function Dashboard() {
26512720
document.body.style.userSelect = 'none';
26522721
}, [sidebarCollapsed, sidebarWidth]);
26532722

2723+
const startCaptionDrag = useCallback((event: React.PointerEvent<HTMLDivElement>) => {
2724+
if (event.button !== 0) {
2725+
return;
2726+
}
2727+
captionDragStartRef.current = {
2728+
pointerX: event.clientX,
2729+
pointerY: event.clientY,
2730+
x: captionOffset.x,
2731+
y: captionOffset.y,
2732+
};
2733+
setCaptionDragging(true);
2734+
document.body.style.cursor = 'grabbing';
2735+
document.body.style.userSelect = 'none';
2736+
}, [captionOffset.x, captionOffset.y]);
2737+
26542738
const resetViewState = useCallback(() => {
26552739
setSidebarCollapsed(false);
26562740
setSidebarWidth(DEFAULT_SIDEBAR_WIDTH);
26572741
setCanvasViewport(DEFAULT_VIEWPORT);
2742+
setCaptionCardScale(DEFAULT_CAPTION_CARD_SCALE);
2743+
setCaptionOffset(DEFAULT_CAPTION_OFFSET);
26582744
const instance = reactFlowRef.current;
26592745
if (instance) {
26602746
void instance.setViewport(DEFAULT_VIEWPORT, { duration: 160 });
26612747
}
26622748
if (typeof window !== 'undefined') {
26632749
window.localStorage.removeItem(CANVAS_VIEWPORT_STORAGE_KEY);
26642750
window.localStorage.removeItem(SIDEBAR_WIDTH_STORAGE_KEY);
2751+
window.localStorage.removeItem(CAPTION_CARD_SCALE_STORAGE_KEY);
2752+
window.localStorage.removeItem(CAPTION_OFFSET_STORAGE_KEY);
26652753
}
26662754
}, []);
26672755

@@ -2983,6 +3071,8 @@ function Dashboard() {
29833071
const viewportDisplay = `x ${Math.round(canvasViewport.x)}, y ${Math.round(canvasViewport.y)}, z ${canvasViewport.zoom.toFixed(2)}, panel ${Math.round(sidebarWidth)}px`;
29843072
const graphFontScalePercent = Math.round(graphFontScale * 100);
29853073
const graphFontScaleDisplay = t(locale, 'graphTextScaleValue').replace('{percent}', String(graphFontScalePercent));
3074+
const captionCardScalePercent = Math.round(captionCardScale * 100);
3075+
const captionCardScaleDisplay = t(locale, 'graphTextScaleValue').replace('{percent}', String(captionCardScalePercent));
29863076
const playSpeedDisplay = t(locale, 'playSpeedValue').replace('{speed}', playSpeed.toFixed(playSpeed % 1 === 0 ? 0 : 2).replace(/\.00$/, '').replace(/(\.\d)0$/, '$1'));
29873077
const playtimeDisplay = formatPlaytime(playtimeElapsedMs);
29883078
const playbackControlTitle = playback.phase === 'running'
@@ -3094,6 +3184,21 @@ function Dashboard() {
30943184
<span className="settings-range-value">{graphFontScaleDisplay}</span>
30953185
</div>
30963186
</label>
3187+
<label className="settings-field">
3188+
<span className="settings-label">{t(locale, 'captionCardSize')}</span>
3189+
<div className="settings-range-row">
3190+
<input
3191+
className="settings-range-input"
3192+
type="range"
3193+
min={MIN_CAPTION_CARD_SCALE}
3194+
max={MAX_CAPTION_CARD_SCALE}
3195+
step={0.05}
3196+
value={captionCardScale}
3197+
onChange={(event) => setCaptionCardScale(Number.parseFloat(event.target.value))}
3198+
/>
3199+
<span className="settings-range-value">{captionCardScaleDisplay}</span>
3200+
</div>
3201+
</label>
30973202
<label className="settings-field">
30983203
<span className="settings-label">{t(locale, 'playSpeed')}</span>
30993204
<div className="settings-range-row">
@@ -3218,10 +3323,21 @@ function Dashboard() {
32183323
<section
32193324
className="canvas-area"
32203325
onWheel={handleCanvasWheel}
3221-
style={{ '--graph-font-scale': String(graphFontScale) } as CSSProperties}
3326+
style={{
3327+
'--graph-font-scale': String(graphFontScale),
3328+
'--caption-card-scale': String(captionCardScale),
3329+
'--caption-font-scale': String(captionCardScale),
3330+
} as CSSProperties}
32223331
>
32233332
{canvasCaptionTitle && (
3224-
<div className={cn("canvas-caption-card", canvasCaptionLeaving && "canvas-caption-card-leaving")}>
3333+
<div
3334+
className={cn("canvas-caption-card", captionDragging && "canvas-caption-card-dragging", canvasCaptionLeaving && "canvas-caption-card-leaving")}
3335+
onPointerDown={startCaptionDrag}
3336+
style={{
3337+
left: `calc(50% + ${captionOffset.x}px)`,
3338+
top: `${18 + captionOffset.y}px`,
3339+
}}
3340+
>
32253341
<span className="canvas-caption-glow" aria-hidden="true" />
32263342
<span className="canvas-caption-aura" aria-hidden="true" />
32273343
<span className="canvas-caption-edge" aria-hidden="true" />

src/index.css

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -210,9 +210,9 @@ body {
210210
top: 18px;
211211
left: 50%;
212212
transform: translateX(-50%);
213-
width: min(520px, calc(100% - 220px));
214-
padding: 20px 26px 22px;
215-
border-radius: 26px;
213+
width: min(calc(520px * var(--caption-card-scale, 1)), calc(100% - 220px));
214+
padding: calc(20px * var(--caption-card-scale, 1)) calc(26px * var(--caption-card-scale, 1)) calc(22px * var(--caption-card-scale, 1));
215+
border-radius: calc(26px * var(--caption-card-scale, 1));
216216
background:
217217
radial-gradient(circle at 14% -4%, rgba(56, 189, 248, 0.36), transparent 32%),
218218
radial-gradient(circle at 84% 8%, rgba(99, 102, 241, 0.18), transparent 30%),
@@ -232,10 +232,12 @@ body {
232232
canvas-caption-in 260ms cubic-bezier(0.16, 0.84, 0.26, 1),
233233
canvas-caption-aura-breathe 4.2s ease-in-out infinite;
234234
transform-origin: top center;
235-
pointer-events: none;
235+
pointer-events: auto;
236236
overflow: hidden;
237237
isolation: isolate;
238238
backdrop-filter: blur(10px);
239+
cursor: grab;
240+
user-select: none;
239241
}
240242

241243
.canvas-caption-content {
@@ -244,28 +246,28 @@ body {
244246
display: grid;
245247
gap: 4px;
246248
justify-items: center;
247-
padding: 0 8px 0;
249+
padding: 0 calc(8px * var(--caption-card-scale, 1)) 0;
248250
animation: canvas-caption-content-in 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
249251
}
250252

251253
.canvas-caption-kicker {
252254
justify-self: center;
253-
padding: 6px 14px 7px;
255+
padding: calc(6px * var(--caption-card-scale, 1)) calc(14px * var(--caption-card-scale, 1)) calc(7px * var(--caption-card-scale, 1));
254256
margin-top: -10px;
255257
border-radius: 999px;
256258
background: linear-gradient(180deg, rgba(56, 189, 248, 0.18), rgba(255, 255, 255, 0.4));
257259
box-shadow:
258260
inset 0 0 0 1px rgba(125, 211, 252, 0.32),
259261
0 0 20px rgba(14, 165, 233, 0.16);
260-
font-size: calc(0.72rem * var(--graph-font-scale, 1));
262+
font-size: calc(0.72rem * var(--caption-font-scale, 1));
261263
font-weight: 900;
262264
letter-spacing: 0.2em;
263265
text-transform: uppercase;
264266
color: #0369a1;
265267
}
266268

267269
.canvas-caption-title {
268-
font-size: calc(2.5rem * var(--graph-font-scale, 1));
270+
font-size: calc(2.5rem * var(--caption-font-scale, 1));
269271
font-weight: 900;
270272
line-height: 0.92;
271273
letter-spacing: -0.055em;
@@ -281,6 +283,10 @@ body {
281283
letter-spacing: 0.02em;
282284
}
283285

286+
.canvas-caption-card-dragging {
287+
cursor: grabbing;
288+
}
289+
284290
.canvas-caption-card-leaving {
285291
animation: canvas-caption-out 220ms cubic-bezier(0.4, 0, 0.2, 1) forwards;
286292
}
@@ -306,7 +312,7 @@ body {
306312
.canvas-caption-aura {
307313
z-index: 1;
308314
inset: -16px;
309-
border-radius: 36px;
315+
border-radius: calc(36px * var(--caption-card-scale, 1));
310316
background:
311317
radial-gradient(ellipse at 14% 34%, rgba(56, 189, 248, 0.28), transparent 34%),
312318
radial-gradient(ellipse at 84% 28%, rgba(129, 140, 248, 0.24), transparent 32%),

src/locales/catalog.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"ui.settings.showScript": { "en": "Show script", "zh-CN": "显示脚本" },
2525
"ui.settings.hideScript": { "en": "Hide script", "zh-CN": "隐藏脚本" },
2626
"ui.settings.graphTextSize": { "en": "Graph Text Size", "zh-CN": "图谱文字大小" },
27+
"ui.settings.captionCardSize": { "en": "Caption Size", "zh-CN": "标题大小" },
2728
"ui.settings.graphTextScaleValue": { "en": "{percent}%", "zh-CN": "{percent}%" },
2829
"ui.settings.playSpeed": { "en": "Play Speed", "zh-CN": "播放速度" },
2930
"ui.settings.playSpeedValue": { "en": "{speed}x", "zh-CN": "{speed}x" },
@@ -61,7 +62,7 @@
6162
"narrative.stage1Summary": { "en": "Discover And Assign Courier", "zh-CN": "发现并指派配送机器人" },
6263
"narrative.stage2Summary": { "en": "Notify And Coordinate Delivery", "zh-CN": "通知用户并协调配送" },
6364
"narrative.stage3Summary": { "en": "Verify And Close Handoff", "zh-CN": "校验并完成交接" },
64-
"narrative.stage0GateTitle": { "en": "Family domain created and ready", "zh-CN": "家庭域已创建并准备就绪" },
65+
"narrative.stage0GateTitle": { "en": "Family domain created and ready", "zh-CN": "家庭域创建成功" },
6566
"narrative.stage0GateBody": { "en": "- **Family Domain** is now active.\n- Members: **UE Assistant** and **RobotDog**.\n- Domain ID: `4sg520s2.acn.domain.cmcc`.", "zh-CN": "- **家庭智能体域** 已激活。\n- 成员:**手机终端** 与 **机器狗**。\n- 域 ID:`4sg520s2.acn.domain.cmcc`。" },
6667
"narrative.stage1GateTitle": { "en": "Courier discovered and pickup assigned", "zh-CN": "已发现配送机器人并下发取件任务" },
6768
"narrative.stage1GateBody": { "en": "- **RobotArm** has been selected as the courier.\n- The pickup task has been delivered across both gateways.", "zh-CN": "- 已选择 **送餐机器人** 作为配送者。\n- 取件任务已通过双侧网关完成下发。" },
@@ -99,7 +100,7 @@
99100

100101
"scenario.description.applyDigitalId": { "en": "Apply for a digital ID", "zh-CN": "申请数字身份" },
101102
"scenario.description.createFamilyDomain": { "en": "Create family domain", "zh-CN": "创建家庭域" },
102-
"scenario.description.orderReceivedKitchenPreparing": { "en": "Order received, Kitchen preparing.", "zh-CN": "订单已接收,厨房正在备餐。" },
103+
"scenario.description.orderReceivedKitchenPreparing": { "en": "Order received, Kitchen preparing.", "zh-CN": "订单分配完成" },
103104
"scenario.description.findCourier": { "en": "Find courier", "zh-CN": "寻找配送员" },
104105
"scenario.description.courierPickingUpOrder": { "en": "Courier picking up the order", "zh-CN": "配送员正在取餐" },
105106
"scenario.description.outForDelivery": { "en": "Out for delivery", "zh-CN": "配送途中" },
@@ -110,7 +111,7 @@
110111
"scenario.action.orderFood": { "en": "Order Food", "zh-CN": "下单点餐" },
111112
"scenario.action.agentDiscoveryRequest": { "en": "Agent discovery request", "zh-CN": "Agent 发现请求" },
112113
"scenario.action.pickupAndDelivery": { "en": "Pickup and delivery", "zh-CN": "取餐与配送" },
113-
"scenario.action.pickupAndDeliveryTaskDispatched": { "en": "Pickup and delivery task dispatched to the courier robot.", "zh-CN": "取餐与配送任务已下发至配送机器人。" },
114+
"scenario.action.pickupAndDeliveryTaskDispatched": { "en": "Pickup and delivery task dispatched to the courier robot.", "zh-CN": "配送任务已下发" },
114115
"scenario.action.gisData": { "en": "GIS data", "zh-CN": "GIS 数据" },
115116

116117
"graph.region.core": { "en": "Core Network", "zh-CN": "中国移动" },

0 commit comments

Comments
 (0)