@arronqzy/react-view 1.0.11 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/styles.css +4 -4
- package/package.json +6 -6
- package/src/panel/ReactViewOnlinePreview.tsx +41 -18
- package/src/panel/ReactViewPanel.tsx +52 -2
- package/src/panel/ai/AssistantChatPanel.tsx +15 -41
- package/src/panel/components/PanelConfigSidebar.tsx +442 -352
- package/src/panel/components/WorkspaceConfigSidebar.tsx +8 -0
- package/src/panel/utils/chartOptionBuilder.ts +48 -3
- package/src/panel/utils/outputScale.ts +69 -0
- package/src/panel/utils/panelElementDefaults.ts +0 -1
package/dist/styles.css
CHANGED
|
@@ -1090,6 +1090,10 @@ video {
|
|
|
1090
1090
|
width: 2.25rem;
|
|
1091
1091
|
}
|
|
1092
1092
|
|
|
1093
|
+
.w-\[108px\] {
|
|
1094
|
+
width: 108px;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1093
1097
|
.w-\[220px\] {
|
|
1094
1098
|
width: 220px;
|
|
1095
1099
|
}
|
|
@@ -1098,10 +1102,6 @@ video {
|
|
|
1098
1102
|
width: 280px;
|
|
1099
1103
|
}
|
|
1100
1104
|
|
|
1101
|
-
.w-\[96px\] {
|
|
1102
|
-
width: 96px;
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
1105
|
.w-\[min\(92vw\2c 720px\)\] {
|
|
1106
1106
|
width: min(92vw, 720px);
|
|
1107
1107
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arronqzy/react-view",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"files": [
|
|
5
5
|
"src",
|
|
6
6
|
"dist",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"@codemirror/lang-json": "^6.0.1",
|
|
31
31
|
"@codemirror/theme-one-dark": "^6.1.2",
|
|
32
32
|
"@codemirror/view": "^6.36.2",
|
|
33
|
-
"@arronqzy/blueprint-dsl": "1.0.5",
|
|
34
|
-
"@arronqzy/i18n": "0.1.3",
|
|
35
33
|
"@arronqzy/ui": "1.0.5",
|
|
36
|
-
"@arronqzy/react-blueprint": "1.0.
|
|
34
|
+
"@arronqzy/react-blueprint": "1.0.7",
|
|
35
|
+
"@arronqzy/view-table": "0.1.3",
|
|
37
36
|
"@arronqzy/rx-store": "1.0.4",
|
|
38
|
-
"@arronqzy/webllm-assistant": "0.1.
|
|
39
|
-
"@arronqzy/
|
|
37
|
+
"@arronqzy/webllm-assistant": "0.1.7",
|
|
38
|
+
"@arronqzy/blueprint-dsl": "1.0.6",
|
|
39
|
+
"@arronqzy/i18n": "0.1.5"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/react": "^19.0.1",
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
parsePanelLayers,
|
|
36
36
|
resolvePreviewLayerElements,
|
|
37
37
|
} from "./utils/panelStateIO";
|
|
38
|
+
import { applyPreviewSceneFill, readOutputScale } from "./utils/outputScale";
|
|
38
39
|
|
|
39
40
|
const PREVIEW_BOOT_PHASES: PageLifecyclePhase[] = ["mounted"];
|
|
40
41
|
|
|
@@ -76,6 +77,8 @@ export function ReactViewOnlinePreview({
|
|
|
76
77
|
>([]);
|
|
77
78
|
const [layoutRevision, setLayoutRevision] = useState(0);
|
|
78
79
|
const [layoutReady, setLayoutReady] = useState(false);
|
|
80
|
+
const [outputScale, setOutputScale] = useState(() => readOutputScale());
|
|
81
|
+
const [fillScale, setFillScale] = useState({ scaleX: 1, scaleY: 1 });
|
|
79
82
|
|
|
80
83
|
const sceneRef = useRef<HTMLDivElement | null>(null);
|
|
81
84
|
|
|
@@ -179,25 +182,38 @@ export function ReactViewOnlinePreview({
|
|
|
179
182
|
|
|
180
183
|
const displayElements = useMemo(
|
|
181
184
|
() =>
|
|
182
|
-
scopedLayerElements.map((el) =>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
scopedLayerElements.map((el) => {
|
|
186
|
+
const x = el.x - sceneBounds.minX;
|
|
187
|
+
const y = el.y - sceneBounds.minY;
|
|
188
|
+
if (outputScale) return { ...el, x, y };
|
|
189
|
+
return {
|
|
190
|
+
...el,
|
|
191
|
+
x: x * fillScale.scaleX,
|
|
192
|
+
y: y * fillScale.scaleY,
|
|
193
|
+
width: el.width * fillScale.scaleX,
|
|
194
|
+
height: el.height * fillScale.scaleY,
|
|
195
|
+
};
|
|
196
|
+
}),
|
|
197
|
+
[
|
|
198
|
+
fillScale.scaleX,
|
|
199
|
+
fillScale.scaleY,
|
|
200
|
+
outputScale,
|
|
201
|
+
sceneBounds.minX,
|
|
202
|
+
sceneBounds.minY,
|
|
203
|
+
scopedLayerElements,
|
|
204
|
+
]
|
|
188
205
|
);
|
|
189
206
|
|
|
190
207
|
const applySceneFit = useCallback(() => {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
scene.style.transform = `scale(${scaleX}, ${scaleY})`;
|
|
208
|
+
const enabled = readOutputScale();
|
|
209
|
+
setOutputScale(enabled);
|
|
210
|
+
const fill = applyPreviewSceneFill(
|
|
211
|
+
sceneRef.current,
|
|
212
|
+
sceneBounds.width,
|
|
213
|
+
sceneBounds.height,
|
|
214
|
+
enabled
|
|
215
|
+
);
|
|
216
|
+
setFillScale({ scaleX: fill.scaleX, scaleY: fill.scaleY });
|
|
201
217
|
setLayoutRevision((v) => v + 1);
|
|
202
218
|
notifyPreviewLayoutChanged();
|
|
203
219
|
}, [sceneBounds.height, sceneBounds.width]);
|
|
@@ -304,9 +320,16 @@ export function ReactViewOnlinePreview({
|
|
|
304
320
|
id="preview-scene"
|
|
305
321
|
className="relative shrink-0 origin-top-left"
|
|
306
322
|
style={{
|
|
307
|
-
width:
|
|
308
|
-
|
|
323
|
+
width: outputScale
|
|
324
|
+
? sceneBounds.width
|
|
325
|
+
: sceneBounds.width * fillScale.scaleX,
|
|
326
|
+
height: outputScale
|
|
327
|
+
? sceneBounds.height
|
|
328
|
+
: sceneBounds.height * fillScale.scaleY,
|
|
309
329
|
transformOrigin: "left top",
|
|
330
|
+
transform: outputScale
|
|
331
|
+
? `scale(${fillScale.scaleX}, ${fillScale.scaleY})`
|
|
332
|
+
: "none",
|
|
310
333
|
}}
|
|
311
334
|
>
|
|
312
335
|
<ElementsLayer
|
|
@@ -14,6 +14,7 @@ import { ElementsLayer } from "./components/ElementsLayer";
|
|
|
14
14
|
import { SelectLayer } from "./components/SelectLayer";
|
|
15
15
|
import { MoveableLayer } from "./components/MoveableLayer";
|
|
16
16
|
import { buildChartOption, CHART_TYPES } from "./utils/chartOptionBuilder";
|
|
17
|
+
import { readOutputScale, writeOutputScale } from "./utils/outputScale";
|
|
17
18
|
import { MaterialSidebar } from "./components/MaterialSidebar";
|
|
18
19
|
import { AssistantChatPanel } from "./ai/AssistantChatPanel";
|
|
19
20
|
import {
|
|
@@ -94,6 +95,7 @@ import {
|
|
|
94
95
|
EmptyTitle,
|
|
95
96
|
Input,
|
|
96
97
|
Menubar,
|
|
98
|
+
MenubarCheckboxItem,
|
|
97
99
|
MenubarContent,
|
|
98
100
|
MenubarItem,
|
|
99
101
|
MenubarMenu,
|
|
@@ -460,6 +462,7 @@ export function ReactViewPanel({
|
|
|
460
462
|
const [titleIconZoom, setTitleIconZoom] = useState(1.6);
|
|
461
463
|
const [pendingPreviewLayerId, setPendingPreviewLayerId] = useState<string | null>(null);
|
|
462
464
|
const [panelFontSize, setPanelFontSize] = useState<"sm" | "md" | "lg">("md");
|
|
465
|
+
const [outputScale, setOutputScale] = useState(() => readOutputScale());
|
|
463
466
|
const [blueprintOpen, setBlueprintOpen] = useState(false);
|
|
464
467
|
const [assistantOpen, setAssistantOpen] = useState(false);
|
|
465
468
|
const [blueprintGraph, setBlueprintGraph] = useState(() => BlueprintGraph.empty());
|
|
@@ -570,6 +573,10 @@ export function ReactViewPanel({
|
|
|
570
573
|
}
|
|
571
574
|
}, [panelFontSize]);
|
|
572
575
|
|
|
576
|
+
useEffect(() => {
|
|
577
|
+
writeOutputScale(outputScale);
|
|
578
|
+
}, [outputScale]);
|
|
579
|
+
|
|
573
580
|
useEffect(() => {
|
|
574
581
|
try {
|
|
575
582
|
const stored = localStorage.getItem(TITLE_ICON_STORAGE_KEY) ?? "";
|
|
@@ -1784,16 +1791,49 @@ export function ReactViewPanel({
|
|
|
1784
1791
|
});
|
|
1785
1792
|
}
|
|
1786
1793
|
var scene = document.getElementById("preview-scene");
|
|
1794
|
+
var root = document.getElementById("preview-root");
|
|
1795
|
+
var outputScale = ${outputScale ? "true" : "false"};
|
|
1796
|
+
function rememberBox(node) {
|
|
1797
|
+
if (node.dataset.origLeft != null) return;
|
|
1798
|
+
node.dataset.origLeft = String(parseFloat(node.style.left) || 0);
|
|
1799
|
+
node.dataset.origTop = String(parseFloat(node.style.top) || 0);
|
|
1800
|
+
node.dataset.origWidth = String(parseFloat(node.style.width) || node.offsetWidth || 0);
|
|
1801
|
+
node.dataset.origHeight = String(parseFloat(node.style.height) || node.offsetHeight || 0);
|
|
1802
|
+
}
|
|
1787
1803
|
function fitScene() {
|
|
1788
1804
|
if (!scene) return;
|
|
1789
1805
|
var vw = window.innerWidth || 1;
|
|
1790
1806
|
var vh = window.innerHeight || 1;
|
|
1791
1807
|
var sw = ${sceneWidth} || 1;
|
|
1792
1808
|
var sh = ${sceneHeight} || 1;
|
|
1793
|
-
// 铺满预览窗口:宽高分别缩放,保证内容贴住四边
|
|
1794
1809
|
var scaleX = vw / sw;
|
|
1795
1810
|
var scaleY = vh / sh;
|
|
1796
|
-
|
|
1811
|
+
if (root) root.style.overflow = "hidden";
|
|
1812
|
+
var nodes = scene.querySelectorAll("[data-element-id]");
|
|
1813
|
+
if (outputScale) {
|
|
1814
|
+
scene.style.width = sw + "px";
|
|
1815
|
+
scene.style.height = sh + "px";
|
|
1816
|
+
scene.style.transform = "translate(0px,0px) scale(" + scaleX + "," + scaleY + ")";
|
|
1817
|
+
nodes.forEach(function (node) {
|
|
1818
|
+
rememberBox(node);
|
|
1819
|
+
node.style.left = node.dataset.origLeft + "px";
|
|
1820
|
+
node.style.top = node.dataset.origTop + "px";
|
|
1821
|
+
node.style.width = node.dataset.origWidth + "px";
|
|
1822
|
+
node.style.height = node.dataset.origHeight + "px";
|
|
1823
|
+
});
|
|
1824
|
+
return;
|
|
1825
|
+
}
|
|
1826
|
+
// 块仍按视口撑满,但不对整棵内容做 transform,字体保持原尺寸
|
|
1827
|
+
scene.style.width = vw + "px";
|
|
1828
|
+
scene.style.height = vh + "px";
|
|
1829
|
+
scene.style.transform = "none";
|
|
1830
|
+
nodes.forEach(function (node) {
|
|
1831
|
+
rememberBox(node);
|
|
1832
|
+
node.style.left = (Number(node.dataset.origLeft) * scaleX) + "px";
|
|
1833
|
+
node.style.top = (Number(node.dataset.origTop) * scaleY) + "px";
|
|
1834
|
+
node.style.width = (Number(node.dataset.origWidth) * scaleX) + "px";
|
|
1835
|
+
node.style.height = (Number(node.dataset.origHeight) * scaleY) + "px";
|
|
1836
|
+
});
|
|
1797
1837
|
}
|
|
1798
1838
|
fitScene();
|
|
1799
1839
|
window.addEventListener("resize", fitScene);
|
|
@@ -2013,6 +2053,14 @@ export function ReactViewPanel({
|
|
|
2013
2053
|
<MenubarRadioItem value="zh-CN">{t("panel.theme.zhCN")}</MenubarRadioItem>
|
|
2014
2054
|
<MenubarRadioItem value="en-US">{t("panel.theme.enUS")}</MenubarRadioItem>
|
|
2015
2055
|
</MenubarRadioGroup>
|
|
2056
|
+
<MenubarSeparator />
|
|
2057
|
+
<MenubarCheckboxItem
|
|
2058
|
+
checked={outputScale}
|
|
2059
|
+
onCheckedChange={(checked) => setOutputScale(checked === true)}
|
|
2060
|
+
title={t("panel.menubar.outputScaleHint")}
|
|
2061
|
+
>
|
|
2062
|
+
{t("panel.menubar.outputScale")}
|
|
2063
|
+
</MenubarCheckboxItem>
|
|
2016
2064
|
</MenubarContent>
|
|
2017
2065
|
</MenubarMenu>
|
|
2018
2066
|
</Menubar>
|
|
@@ -3176,6 +3224,8 @@ export function ReactViewPanel({
|
|
|
3176
3224
|
configFocus={sidebarConfigFocus}
|
|
3177
3225
|
executionLog={executionLogView}
|
|
3178
3226
|
selectedBlueprintNode={selectedBlueprintNode}
|
|
3227
|
+
blueprintGraphNodes={blueprintGraph.document.nodes}
|
|
3228
|
+
blueprintGraphEdges={blueprintGraph.document.edges}
|
|
3179
3229
|
allowFalseSignalPropagation={
|
|
3180
3230
|
blueprintGraph.document.allowFalseSignalPropagation ?? false
|
|
3181
3231
|
}
|
|
@@ -3,18 +3,17 @@ import {
|
|
|
3
3
|
ASSISTANT_MODEL_OPTIONS,
|
|
4
4
|
DEFAULT_MODEL_ID,
|
|
5
5
|
WebLLMAssistantRuntime,
|
|
6
|
-
buildSystemPrompt,
|
|
7
6
|
isWebGPUAvailable,
|
|
8
7
|
looksLikePureQuestion,
|
|
9
|
-
parseAssistantAction,
|
|
10
8
|
inferPanelAddFromUserText,
|
|
11
9
|
runAgentLoop,
|
|
12
10
|
buildEditorContext,
|
|
13
11
|
tryLocalFaqReply,
|
|
12
|
+
buildChatSystemPrompt,
|
|
14
13
|
buildForceReplyPrompt,
|
|
14
|
+
unwrapChatReply,
|
|
15
15
|
buildPriorChatMessages,
|
|
16
16
|
buildRecentDialogSummary,
|
|
17
|
-
looksLikeUnclearIntent,
|
|
18
17
|
UNCLEAR_INTENT_REPLY,
|
|
19
18
|
inferLocalEditorPlan,
|
|
20
19
|
type ChatMessage,
|
|
@@ -345,15 +344,6 @@ export function AssistantChatPanel({
|
|
|
345
344
|
return;
|
|
346
345
|
}
|
|
347
346
|
|
|
348
|
-
// Vague greetings / short junk — ask instead of mutating.
|
|
349
|
-
if (looksLikeUnclearIntent(trimmed)) {
|
|
350
|
-
pushMessage({
|
|
351
|
-
role: "assistant",
|
|
352
|
-
content: t("panel.ai.unclearIntent") || UNCLEAR_INTENT_REPLY,
|
|
353
|
-
});
|
|
354
|
-
return;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
347
|
// High-confidence local tools (add / resize / delete / save …) — skip model.
|
|
358
348
|
const selectedIds = depsRef.current.getSelectedIds() ?? [];
|
|
359
349
|
const localPlan = inferLocalEditorPlan(trimmed, { selectedIds });
|
|
@@ -402,41 +392,20 @@ export function AssistantChatPanel({
|
|
|
402
392
|
const runtime = runtimeRef.current!;
|
|
403
393
|
|
|
404
394
|
if (looksLikePureQuestion(trimmed)) {
|
|
405
|
-
const ctx = buildEditorContext({
|
|
406
|
-
...buildObservation(),
|
|
407
|
-
userGoal: trimmed,
|
|
408
|
-
recentDialog,
|
|
409
|
-
});
|
|
410
395
|
const history: ChatMessage[] = [
|
|
411
|
-
{ role: "system", content:
|
|
396
|
+
{ role: "system", content: buildChatSystemPrompt() },
|
|
412
397
|
...priorChat,
|
|
413
398
|
{ role: "user", content: trimmed },
|
|
414
399
|
];
|
|
415
|
-
let
|
|
416
|
-
if (
|
|
417
|
-
// Small models often mutate the page on Q&A — force a reply-only turn.
|
|
418
|
-
history.push({
|
|
419
|
-
role: "assistant",
|
|
420
|
-
content: JSON.stringify(parsed.action),
|
|
421
|
-
});
|
|
400
|
+
let message = unwrapChatReply(await runtime.chat(history));
|
|
401
|
+
if (!message) {
|
|
422
402
|
history.push({ role: "user", content: buildForceReplyPrompt() });
|
|
423
|
-
|
|
403
|
+
message = unwrapChatReply(await runtime.chat(history));
|
|
424
404
|
}
|
|
425
|
-
if (!parsed.ok) {
|
|
426
|
-
pushMessage({
|
|
427
|
-
role: "assistant",
|
|
428
|
-
content: `${t("panel.ai.parseFailed")}\n\n\`\`\`\n${parsed.rawText.slice(0, 800)}\n\`\`\``,
|
|
429
|
-
});
|
|
430
|
-
return;
|
|
431
|
-
}
|
|
432
|
-
if (parsed.action.type === "reply") {
|
|
433
|
-
pushMessage({ role: "assistant", content: parsed.action.message });
|
|
434
|
-
return;
|
|
435
|
-
}
|
|
436
|
-
// Still not a reply — refuse to mutate; ask clearly instead.
|
|
437
405
|
pushMessage({
|
|
438
406
|
role: "assistant",
|
|
439
|
-
content:
|
|
407
|
+
content:
|
|
408
|
+
message || t("panel.ai.unclearIntent") || UNCLEAR_INTENT_REPLY,
|
|
440
409
|
});
|
|
441
410
|
return;
|
|
442
411
|
}
|
|
@@ -625,12 +594,17 @@ export function AssistantChatPanel({
|
|
|
625
594
|
onValueChange={setModelId}
|
|
626
595
|
disabled={busy || status === "loading"}
|
|
627
596
|
>
|
|
628
|
-
<SelectTrigger className="h-8 w-[
|
|
597
|
+
<SelectTrigger className="h-8 w-[108px] rounded-md border-border bg-background text-[12px] shadow-none">
|
|
629
598
|
<SelectValue />
|
|
630
599
|
</SelectTrigger>
|
|
631
600
|
<SelectContent position="popper" sideOffset={6}>
|
|
632
601
|
{ASSISTANT_MODEL_OPTIONS.map((opt) => (
|
|
633
|
-
<SelectItem
|
|
602
|
+
<SelectItem
|
|
603
|
+
key={opt.id}
|
|
604
|
+
value={opt.id}
|
|
605
|
+
className="text-[12px]"
|
|
606
|
+
title={t(opt.hintKey)}
|
|
607
|
+
>
|
|
634
608
|
{t(opt.labelKey)}
|
|
635
609
|
</SelectItem>
|
|
636
610
|
))}
|