테마 적용
CI / test (push) Failing after 5m22s
CI / e2e (push) Has been skipped
CI / pr_and_merge (push) Has been skipped

This commit is contained in:
2025-12-09 18:53:21 +09:00
parent 9d8c4538c7
commit 676e58cad7
71 changed files with 3394 additions and 498 deletions
+44 -37
View File
@@ -1070,8 +1070,8 @@ function EditorPageInner() {
});
return (
<main className="h-screen flex flex-col overflow-hidden">
<header className="border-b border-slate-800 px-6 py-4 flex items-center justify-between relative z-20 bg-slate-950/80 backdrop-blur">
<main className="h-screen flex flex-col overflow-hidden bg-slate-100 text-slate-900 dark:bg-slate-950 dark:text-slate-50">
<header className="border-b border-slate-200 px-6 py-4 flex items-center justify-between relative z-20 bg-white/80 backdrop-blur dark:border-slate-800 dark:bg-slate-950/80">
<div className="flex items-center gap-3">
<span className="inline-flex items-center justify-center w-8 h-8 rounded-full bg-slate-900 border border-sky-700 shadow-sm">
<Pencil className="w-4 h-4 text-sky-400" aria-hidden="true" />
@@ -1233,12 +1233,12 @@ function EditorPageInner() {
{activeModal === "project" && (
<div className="fixed inset-0 z-30 flex items-center justify-center bg-black/60">
<div className="w-full max-w-md rounded-lg border border-slate-700 bg-slate-900 p-4 text-xs text-slate-100 shadow-xl">
<div className="w-full max-w-md rounded-lg border border-slate-200 bg-white p-4 text-xs text-slate-900 shadow-xl dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-medium"> / </h3>
<button
type="button"
className="text-slate-400 hover:text-slate-100 text-sm"
className="text-slate-400 hover:text-slate-700 text-sm dark:hover:text-slate-100"
onClick={() => setActiveModal(null)}
>
@@ -1246,17 +1246,17 @@ function EditorPageInner() {
</div>
<div className="space-y-2">
<label className="flex flex-col gap-1">
<span className="text-slate-400"> </span>
<span className="text-slate-600 dark:text-slate-400"> </span>
<input
className="w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 text-xs outline-none focus:border-sky-500"
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={projectConfig.title ?? ""}
onChange={(e) => updateProjectConfig({ title: e.target.value })}
/>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-400"> (: my-landing)</span>
<span className="text-slate-600 dark:text-slate-400"> (: my-landing)</span>
<input
className="w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 text-xs outline-none focus:border-sky-500"
className="w-full rounded border border-slate-300 bg-white px-2 py-1 text-xs text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={projectConfig.slug ?? ""}
onChange={(e) => updateProjectConfig({ slug: e.target.value })}
/>
@@ -1264,21 +1264,21 @@ function EditorPageInner() {
<div className="flex gap-2">
<button
type="button"
className="flex-1 rounded border border-sky-700 bg-sky-950 px-2 py-1 text-sky-100 hover:bg-sky-900"
className="flex-1 rounded border border-sky-500 bg-sky-50 px-2 py-1 text-sky-700 hover:bg-sky-100 dark:border-sky-700 dark:bg-sky-950 dark:text-sky-100 dark:hover:bg-sky-900"
onClick={handleSaveProject}
>
( + )
</button>
<button
type="button"
className="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-slate-100 hover:bg-slate-800"
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
onClick={handleLoadProject}
>
</button>
</div>
{projectMessage && (
<p className="text-[11px] text-slate-300 mt-1">{projectMessage}</p>
<p className="text-[11px] text-slate-600 mt-1 dark:text-slate-300">{projectMessage}</p>
)}
</div>
</div>
@@ -1289,12 +1289,17 @@ function EditorPageInner() {
{activeModal === "json" && (
<div className="fixed inset-0 z-30 flex items-center justify-center bg-black/60">
<div className="w-full max-w-2xl rounded-lg border border-slate-700 bg-slate-900 p-4 text-xs text-slate-100 shadow-xl">
<div className="w-full max-w-2xl rounded-lg border border-slate-200 bg-white p-4 text-xs text-slate-900 shadow-xl dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100">
<div className="flex items-center justify-between mb-3">
<h3 className="text-sm font-medium">JSON Export / Import <span className="text-xs text-slate-400">* .</span></h3>
<h3 className="text-sm font-medium">
JSON Export / Import
<span className="text-xs text-slate-500 dark:text-slate-400">
* .
</span>
</h3>
<button
type="button"
className="text-slate-400 hover:text-slate-100 text-sm"
className="text-slate-400 hover:text-slate-700 text-sm dark:hover:text-slate-100"
onClick={() => setActiveModal(null)}
>
@@ -1304,14 +1309,14 @@ function EditorPageInner() {
<div className="flex gap-2 mb-1">
<button
type="button"
className="flex-1 rounded border border-slate-700 bg-slate-950 px-2 py-1 hover:bg-slate-800"
className="flex-1 rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
onClick={handleExportJson}
>
JSON
</button>
<button
type="button"
className="rounded border border-red-700 bg-red-950 px-2 py-1 text-red-100 hover:bg-red-900"
className="rounded border border-red-300 bg-white px-2 py-1 text-red-700 hover:bg-red-50 dark:border-red-700 dark:bg-red-950 dark:text-red-100 dark:hover:bg-red-900"
onClick={handleClearCanvas}
>
@@ -1319,23 +1324,23 @@ function EditorPageInner() {
</div>
<div className="grid grid-cols-2 gap-3">
<label className="flex flex-col gap-1">
<span className="text-slate-400"> JSON</span>
<span className="text-slate-600 dark:text-slate-400"> JSON</span>
<textarea
className="w-full h-48 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[10px] font-mono outline-none focus:border-sky-500"
className="w-full h-48 rounded border border-slate-300 bg-white px-2 py-1 text-[10px] font-mono text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={exportJson}
readOnly
/>
</label>
<label className="flex flex-col gap-1">
<span className="text-slate-400">JSON에서 </span>
<span className="text-slate-600 dark:text-slate-400">JSON에서 </span>
<textarea
className="w-full h-48 rounded border border-slate-700 bg-slate-950 px-2 py-1 text-[10px] font-mono outline-none focus:border-sky-500"
className="w-full h-48 rounded border border-slate-300 bg-white px-2 py-1 text-[10px] font-mono text-slate-900 outline-none focus:border-sky-500 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
value={importJson}
onChange={(e) => setImportJson(e.target.value)}
/>
<button
type="button"
className="mt-1 w-full rounded border border-slate-700 bg-slate-950 px-2 py-1 hover:bg-slate-800"
className="mt-1 w-full rounded border border-slate-300 bg-white px-2 py-1 text-slate-900 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800"
onClick={handleApplyImportJson}
>
JSON
@@ -1509,8 +1514,10 @@ function SortableEditorBlock({
data-selected={isSelected ? "true" : "false"}
aria-selected={isSelected ? "true" : "false"}
className={`rounded border px-3 py-2 cursor-text transition-colors ${alignClass} ${sizeClass} ${leadingClass} ${weightClass} ${colorClass} ${maxWidthClass} ${decorationClass} ${
isSelected ? "border-sky-500 bg-slate-900/80" : "border-slate-700 bg-slate-900"
}`}
isSelected
? "border-sky-500 bg-sky-50 text-slate-900 dark:bg-slate-900/80 dark:text-slate-100"
: "border-slate-200 bg-white text-slate-900 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
}`}
onClick={(event) => {
// 멀티 선택(MVP): Cmd/Ctrl+클릭 시 선택 토글, 그 외에는 단일 선택으로 전환한다.
const isMeta = event.metaKey || event.ctrlKey;
@@ -1541,7 +1548,7 @@ function SortableEditorBlock({
<div className="flex items-start gap-2">
<button
type="button"
className="mt-0.5 h-4 w-4 rounded border border-slate-700 bg-slate-900 text-[10px] flex items-center justify-center text-slate-400 hover:bg-slate-800"
className="mt-0.5 h-4 w-4 rounded border border-slate-300 bg-slate-50 text-[10px] flex items-center justify-center text-slate-500 hover:bg-slate-100 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-400 dark:hover:bg-slate-800"
aria-label="블록 드래그 핸들"
{...listeners}
{...attributes}
@@ -1610,7 +1617,7 @@ function SortableEditorBlock({
className="h-4 w-auto"
/>
) : (
<span className="text-slate-200 shrink-0">{inputProps.label}</span>
<span className="shrink-0">{inputProps.label}</span>
)
)}
{(() => {
@@ -1715,7 +1722,7 @@ function SortableEditorBlock({
className="h-4 w-auto"
/>
) : (
<span className="text-slate-200">{selectProps.label}</span>
<span>{selectProps.label}</span>
)}
<div className="w-full">
<select
@@ -1792,14 +1799,14 @@ function SortableEditorBlock({
className="inline-block max-w-full h-auto"
/>
) : (
<span className="text-slate-200">{radioProps.groupLabel}</span>
<span>{radioProps.groupLabel}</span>
)}
<div style={optionsStyle} className={optionsLayoutClass}>
{options.map((opt) => (
<label key={opt.value} className="pb-form-option">
<input
type="radio"
className="h-4 w-4 border-slate-700 bg-slate-900 text-sky-500"
className="h-4 w-4"
name={radioProps.formFieldName}
value={opt.value}
readOnly
@@ -1863,8 +1870,8 @@ function SortableEditorBlock({
: { rowGap: `${optionGapPx}px` }
: {};
const groupContainerClassName = isInlineLayout
? "flex flex-row items-center text-xs text-slate-200"
: "flex flex-col gap-1 text-xs text-slate-200";
? "flex flex-row items-center text-xs"
: "flex flex-col gap-1 text-xs";
return (
<div className={groupContainerClassName} style={groupContainerStyle}>
@@ -1877,14 +1884,14 @@ function SortableEditorBlock({
className="inline-block max-w-full h-auto"
/>
) : (
<span className="text-slate-200">{checkboxProps.groupLabel}</span>
<span>{checkboxProps.groupLabel}</span>
)}
<div className={optionsLayoutClass} style={optionsStyle}>
{options.map((opt) => (
<label key={opt.value} className="pb-form-option">
<input
type="checkbox"
className="h-4 w-4 rounded border-slate-700 bg-slate-900 text-sky-500"
className="h-4 w-4 rounded"
name={checkboxProps.formFieldName}
value={opt.value}
readOnly
@@ -2050,7 +2057,7 @@ function SortableEditorBlock({
return (
<div
className={`w-full border border-dashed border-slate-700 bg-slate-900/40 rounded flex items-center overflow-hidden ${alignClass}`}
className={`w-full border border-dashed border-slate-700 bg-slate-500/40 rounded flex items-center overflow-hidden ${alignClass}`}
>
{hasSrc ? (
// eslint-disable-next-line @next/next/no-img-element
@@ -2061,7 +2068,7 @@ function SortableEditorBlock({
style={{ ...containerStyle, ...imageStyle }}
/>
) : (
<span className="text-[10px] text-slate-500 px-2 py-4">
<span className="text-[10px] text-slate-900 dark:text-slate-500 px-2 py-4">
URL .
</span>
)}
@@ -2162,7 +2169,7 @@ function SortableEditorBlock({
style={index < nodes.length - 1 ? { marginBottom: gapPx } : undefined}
>
<span className="break-words align-middle">{node.text}</span>
<span className="ml-2 inline-flex items-center gap-1 text-[10px] text-slate-300 align-middle">
<span className="ml-2 inline-flex items-center gap-1 text-[10px] text-slate-900 dark:text-slate-300 align-middle">
<button
type="button"
className="rounded border border-slate-700 px-1 py-0.5 hover:bg-slate-800"
@@ -2367,13 +2374,13 @@ function ColumnDroppable({ id, sectionId, columnId, basis, blocks, renderBlocks,
style={{ flexBasis: basis }}
>
<div
className={`border rounded flex flex-col gap-2 items-stretch justify-start px-2 py-2 bg-slate-950/40 ${
className={`border rounded flex flex-col gap-2 items-stretch justify-start px-2 py-2 bg-slate-100/40 dark:bg-slate-950/40 ${
isActiveColumn ? "border-sky-500" : "border-slate-800/80"
}`}
style={{}}
>
{blocks.length === 0 ? (
<span className="text-[11px] px-2 text-center text-slate-500">
<span className="text-[11px] px-2 text-center text-slate-900 dark:text-slate-300">
{`컬럼 영역 (span ${span}/12)`}
</span>
) : (