fix: style

This commit is contained in:
2026-04-28 10:30:31 +02:00
parent e19cc72ad5
commit a259c3d2e2
3 changed files with 180 additions and 1 deletions
@@ -1,5 +1,6 @@
import {
Box,
Braces,
Download,
Expand,
Keyboard,
@@ -11,12 +12,13 @@ import {
Save,
Undo2,
} from "lucide-react";
import type { TransformMode } from "@/types/editor";
import type { MapNode, TransformMode } from "@/types/editor";
interface EditorControlsProps {
transformMode: TransformMode;
onTransformModeChange: (mode: TransformMode) => void;
selectedNodeIndex: number | null;
mapNodes: MapNode[];
nodesCount: number;
selectedNodeName: string | null;
undoCount: number;
@@ -33,6 +35,7 @@ export function EditorControls({
transformMode,
onTransformModeChange,
selectedNodeIndex,
mapNodes,
nodesCount,
selectedNodeName,
undoCount,
@@ -46,6 +49,7 @@ export function EditorControls({
}: EditorControlsProps): React.JSX.Element {
const cameraPosition = [0, 50, 100];
const viewModeLabel = isPlayerMode ? "View locked" : "Lock view";
const jsonPreview = getJsonPreview(mapNodes, selectedNodeIndex);
return (
<>
@@ -230,7 +234,100 @@ export function EditorControls({
</div>
</dl>
</section>
<section className="editor-json-section" aria-labelledby="json-heading">
<div className="editor-section-heading">
<h3 id="json-heading">JSON</h3>
<span>{jsonPreview.label}</span>
</div>
<pre className="editor-json-view" aria-label={jsonPreview.label}>
{jsonPreview.lines.map((line) => (
<code
key={line.number}
className={line.isSelected ? "is-selected" : undefined}
>
<span>{line.number}</span>
{line.content || " "}
</code>
))}
</pre>
<div className="editor-json-hint">
<Braces size={14} aria-hidden="true" />
{selectedNodeIndex === null
? "Raw map JSON"
: `Selected node ${selectedNodeIndex + 1} raw lines`}
</div>
</section>
</aside>
</>
);
}
interface JsonPreviewLine {
number: number;
content: string;
isSelected: boolean;
}
interface JsonPreview {
label: string;
lines: JsonPreviewLine[];
}
function getJsonPreview(
mapNodes: MapNode[],
selectedNodeIndex: number | null,
): JsonPreview {
const { lines, ranges } = formatMapNodesWithRanges(mapNodes);
if (selectedNodeIndex === null || !ranges[selectedNodeIndex]) {
return {
label: `${lines.length} raw lines`,
lines: lines.map((content, index) => ({
number: index + 1,
content,
isSelected: false,
})),
};
}
const range = ranges[selectedNodeIndex];
const selectedLines = lines.slice(range.start - 1, range.end);
return {
label: `Lines ${range.start}-${range.end}`,
lines: selectedLines.map((content, index) => ({
number: range.start + index,
content,
isSelected: true,
})),
};
}
function formatMapNodesWithRanges(mapNodes: MapNode[]): {
lines: string[];
ranges: Array<{ start: number; end: number }>;
} {
const lines = ["["];
const ranges: Array<{ start: number; end: number }> = [];
mapNodes.forEach((node, index) => {
const objectLines = JSON.stringify(node, null, 2)
.split("\n")
.map((line) => ` ${line}`);
if (index < mapNodes.length - 1) {
objectLines[objectLines.length - 1] += ",";
}
const start = lines.length + 1;
lines.push(...objectLines);
ranges.push({ start, end: lines.length });
});
lines.push("]");
return { lines, ranges };
}
+81
View File
@@ -262,6 +262,8 @@ canvas {
border-radius: 28px;
box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
overflow-y: auto;
display: flex;
flex-direction: column;
backdrop-filter: blur(22px);
scrollbar-width: thin;
scrollbar-color: #3a3a3a transparent;
@@ -541,6 +543,81 @@ canvas {
text-align: right;
}
.editor-json-section {
display: flex;
flex-direction: column;
min-height: 240px;
padding: 14px 12px 12px;
border-top: 1px solid rgba(255, 255, 255, 0.09);
}
.editor-json-view {
flex: 1;
max-height: 320px;
margin: 0;
padding: 8px 0;
overflow: auto;
background: #050505;
border: 1px solid #1f1f1f;
border-radius: 16px;
color: #d7d7d7;
font-family: "SFMono-Regular", "Courier New", monospace;
font-size: 0.72rem;
line-height: 1.55;
scrollbar-width: thin;
scrollbar-color: #3a3a3a transparent;
}
.editor-json-view::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.editor-json-view::-webkit-scrollbar-thumb {
background: #3a3a3a;
border-radius: 999px;
}
.editor-json-view code {
display: grid;
grid-template-columns: 34px max-content;
gap: 10px;
min-width: 100%;
padding: 0 12px;
background: transparent;
color: inherit;
font-family: inherit;
white-space: pre;
}
.editor-json-view code span {
color: #5f5f5f;
text-align: right;
user-select: none;
}
.editor-json-view code.is-selected {
background: #111111;
color: #f2f2f2;
}
.editor-json-view code.is-selected * {
color: #f2f2f2;
}
.editor-json-view code.is-selected span {
color: #8a8a8a;
}
.editor-json-hint {
display: flex;
align-items: center;
gap: 7px;
margin-top: 8px;
color: #8d8d8d;
font-size: 0.74rem;
}
@media (max-width: 768px) {
.editor-error h2 {
font-size: 1.5rem;
@@ -567,4 +644,8 @@ canvas {
max-height: 46vh;
border-radius: 22px;
}
.editor-json-section {
min-height: 180px;
}
}
+1
View File
@@ -173,6 +173,7 @@ export function EditorPage(): React.JSX.Element {
transformMode={transformMode}
onTransformModeChange={handleTransformModeChange}
selectedNodeIndex={selectedNodeIndex}
mapNodes={sceneData.mapNodes}
nodesCount={sceneData.mapNodes.length}
selectedNodeName={
selectedNodeIndex !== null && sceneData.mapNodes[selectedNodeIndex]