fix(cafe24): 소스 들여쓰기 정상화 + 줄 번호·가로 스크롤 편집기
1) 들여쓰기가 안 되던 원인 인라인 요소가 여러 줄에 걸쳐 있으면 그 구간을 하나의 텍스트 덩어리로 다뤄 첫 줄에만 들여쓰기를 붙이고 있었다. 그래서 <img> 가 한 줄에 하나씩 적힌 상세페이지에서 두 번째 <img> 부터 1열에 붙어 나왔다. 원문 줄바꿈을 살리면서 각 줄을 현재 깊이로 들여쓰도록 고쳤다. 줄 앞 공백은 렌더링에 영향이 없으므로 안전하다. 상세페이지는 <img> 를 한 줄에 하나씩 적어두는 경우가 많고 그 모양이 저자의 의도라, 한 줄로 합치는 것보다 살리는 쪽이 읽기 좋다. 주석도 줄을 강제로 나누지 않게 바꿨다. `<!-- 대파_타임랩스 --><img ...>` 처럼 바로 뒤 요소를 설명하는 주석이 많아서, 나누면 라벨과 대상이 떨어져 오히려 읽기 나빠진다. 빈 줄은 구획 표시로 한 줄까지 유지한다. 이 과정에서 멱등성이 다시 깨지는 것을 테스트가 잡았다 — 텍스트 끝에 남은 "\n " 조각이 매번 빈 줄로 바뀌고 있었고, 양 끝 공백을 함께 제거하도록 고쳤다. 2) 줄 번호와 가로 스크롤 전문 편집기처럼 줄 번호 칸을 넣었다. 줄바꿈을 허용하면 한 논리 줄이 여러 행이 되어 번호가 어긋나므로, 줄바꿈을 끄고(white-space:pre + wrap=off) 가로로 스크롤한다. 번호 칸은 position:sticky 라 가로로 스크롤해도 왼쪽에 남는다. 크기 계산도 단순해졌다. <pre> 가 흐름에 남아 상자 크기를 정하고 textarea 가 inset:0 으로 그 위를 덮는다 — JS 로 높이를 맞추지 않으니 어긋날 여지가 없다. 색상 계열은 그대로 뒀다(태그 초록·속성 갈색·값 남색·주석 회색). 검증: 유닛테스트 45개 통과(신규 4개 — 모든 줄 들여쓰기, 주석과 요소 붙임, 빈 줄 1개 유지, 실제 상세페이지 모양 멱등). 브라우저 실측: 줄 번호 개수가 줄 수와 일치(18/18, 19/19), 번호와 코드의 세로 위치 일치, 긴 줄에서 편집기 안에서만 가로 스크롤(1920 > 716)되고 페이지는 가로 스크롤 없음, 400px 스크롤 후에도 번호 칸 고정, textarea 내부 스크롤 0(두 층 정렬 유지). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -241,12 +241,29 @@ def _format_html(source: str, indent: str) -> str:
|
|||||||
return indent * min(max(level, 0), _MAX_INDENT)
|
return indent * min(max(level, 0), _MAX_INDENT)
|
||||||
|
|
||||||
def flush() -> None:
|
def flush() -> None:
|
||||||
"""모아둔 인라인/텍스트를 한 줄로 내보낸다(빈 줄은 버린다)."""
|
"""모아둔 인라인/텍스트를 내보낸다.
|
||||||
|
|
||||||
|
원문에 이미 있던 줄바꿈은 **그대로 살린다.** 이미지가 한 줄에 하나씩 적혀
|
||||||
|
있으면 그 모양이 저자의 의도이고, 한 줄로 합치면 오히려 읽기 어려워진다.
|
||||||
|
각 줄마다 현재 깊이로 들여쓴다(줄 앞 공백은 렌더링에 영향이 없다).
|
||||||
|
빈 줄은 연속 한 개까지만 남겨 구획을 유지한다.
|
||||||
|
"""
|
||||||
nonlocal buffer
|
nonlocal buffer
|
||||||
|
# 양 끝 공백을 함께 제거한다. `"\n"` 만 벗기면 끝에 남은 `"\n "` 조각이
|
||||||
|
# 빈 줄로 바뀌어 실행마다 빈 줄이 하나씩 늘어난다(멱등 깨짐).
|
||||||
|
# 블록 태그 경계의 공백은 렌더링에 영향이 없으므로 제거해도 안전하다.
|
||||||
text = buffer.strip()
|
text = buffer.strip()
|
||||||
if text:
|
|
||||||
lines.append(pad(depth) + text)
|
|
||||||
buffer = ""
|
buffer = ""
|
||||||
|
if not text:
|
||||||
|
return
|
||||||
|
for raw_line in text.split("\n"):
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line:
|
||||||
|
# 문서 맨 앞이나 빈 줄 뒤에는 빈 줄을 더하지 않는다.
|
||||||
|
if lines and lines[-1] != "":
|
||||||
|
lines.append("")
|
||||||
|
continue
|
||||||
|
lines.append(pad(depth) + line)
|
||||||
|
|
||||||
position = 0
|
position = 0
|
||||||
while True:
|
while True:
|
||||||
@@ -259,10 +276,12 @@ def _format_html(source: str, indent: str) -> str:
|
|||||||
position = match.end()
|
position = match.end()
|
||||||
raw = match.group(0)
|
raw = match.group(0)
|
||||||
|
|
||||||
# 주석·DOCTYPE 등은 한 줄 차지
|
# 주석·DOCTYPE 등은 흐름에 그대로 둔다.
|
||||||
|
# 상세페이지에는 `<!-- 대파_타임랩스 --><img ...>` 처럼 바로 뒤 요소를
|
||||||
|
# 설명하는 주석이 많다. 줄을 강제로 나누면 라벨과 대상이 떨어져 오히려
|
||||||
|
# 읽기 나빠진다. 원문에서 줄이 나뉘어 있었다면 flush 가 그 줄바꿈을 살린다.
|
||||||
if match.group("comment") or match.group("cdata") or match.group("decl"):
|
if match.group("comment") or match.group("cdata") or match.group("decl"):
|
||||||
flush()
|
buffer += raw
|
||||||
lines.append(pad(depth) + raw.strip())
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
name = (match.group("name") or "").lower()
|
name = (match.group("name") or "").lower()
|
||||||
|
|||||||
@@ -65,12 +65,18 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# 색칠된 <pre> 위에 투명한 <textarea> 를 겹쳐 문법 강조를 만든다.
|
{# 색칠된 <pre> 위에 투명한 <textarea> 를 겹쳐 문법 강조를 만든다.
|
||||||
두 요소의 글자 위치가 어긋나면 안 되므로 폰트·여백은 CSS 에서 함께 관리한다. #}
|
<pre> 가 크기를 정하고 <textarea> 는 inset:0 으로 그 위를 정확히 덮는다.
|
||||||
|
줄바꿈을 하지 않고(wrap=off) 가로로 스크롤하므로 줄 번호가 항상 맞는다.
|
||||||
|
두 요소의 폰트·여백이 다르면 글자가 어긋난다 — CSS 에서 함께 관리한다. #}
|
||||||
<div class="cf24-code" id="cf24-code-pc">
|
<div class="cf24-code" id="cf24-code-pc">
|
||||||
<pre class="cf24-code-hl" aria-hidden="true"><code id="cf24-hl-pc"></code></pre>
|
<div class="cf24-code-rows">
|
||||||
<textarea id="cf24-html-pc" class="cf24-code-input" name="html"
|
<div class="cf24-gutter" id="cf24-gutter-pc" aria-hidden="true"></div>
|
||||||
spellcheck="false" autocapitalize="off" autocorrect="off"
|
<div class="cf24-code-body">
|
||||||
wrap="soft">{{ html_pc }}</textarea>
|
<pre class="cf24-code-hl" aria-hidden="true"><code id="cf24-hl-pc"></code></pre>
|
||||||
|
<textarea id="cf24-html-pc" class="cf24-code-input" name="html" wrap="off"
|
||||||
|
spellcheck="false" autocapitalize="off" autocorrect="off">{{ html_pc }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814f" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814g" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
@@ -148,27 +148,35 @@
|
|||||||
function setupCodeEditor() {
|
function setupCodeEditor() {
|
||||||
var ta = document.getElementById("cf24-html-pc");
|
var ta = document.getElementById("cf24-html-pc");
|
||||||
var hl = document.getElementById("cf24-hl-pc");
|
var hl = document.getElementById("cf24-hl-pc");
|
||||||
|
var gutter = document.getElementById("cf24-gutter-pc");
|
||||||
if (!ta || !hl) return;
|
if (!ta || !hl) return;
|
||||||
|
|
||||||
var timer = null;
|
var timer = null;
|
||||||
|
|
||||||
|
function renderGutter(count) {
|
||||||
|
if (!gutter || gutter.dataset.lines === String(count)) return;
|
||||||
|
var out = "";
|
||||||
|
for (var i = 1; i <= count; i++) out += i + "\n";
|
||||||
|
gutter.textContent = out;
|
||||||
|
gutter.dataset.lines = String(count);
|
||||||
|
}
|
||||||
|
|
||||||
function repaint() {
|
function repaint() {
|
||||||
// 마지막 줄이 잘리지 않게 개행을 하나 덧붙인다(<pre> 특성).
|
// 마지막 줄이 잘리지 않게 개행을 하나 덧붙인다(<pre> 특성).
|
||||||
if (ta.value.length > HL_LIMIT) hl.textContent = ta.value + "\n";
|
if (ta.value.length > HL_LIMIT) hl.textContent = ta.value + "\n";
|
||||||
else hl.innerHTML = paintHtml(ta.value) + "\n";
|
else hl.innerHTML = paintHtml(ta.value) + "\n";
|
||||||
}
|
renderGutter(ta.value.split("\n").length);
|
||||||
function resize() {
|
// 크기는 <pre> 가 정하고 textarea 가 그 위를 덮는다. 다시 칠하는 사이
|
||||||
// 색칠된 <pre> 는 같은 내용·같은 스타일이라 이것이 진짜 콘텐츠 높이다.
|
// 내부 스크롤이 생겼으면 되돌려 두 층의 정렬을 유지한다.
|
||||||
// textarea 의 scrollHeight 를 쓰면 브라우저마다 한두 줄 더 잡혀 두 층의
|
|
||||||
// 글자 위치가 어긋난다(실측 830 vs 792).
|
|
||||||
var target = hl.parentElement.scrollHeight;
|
|
||||||
if (target > 0) ta.style.height = target + "px";
|
|
||||||
// 높이가 잠깐 부족했던 사이 내부 스크롤이 생겼으면 되돌린다(두 층 정렬 유지).
|
|
||||||
ta.scrollTop = 0;
|
ta.scrollTop = 0;
|
||||||
|
ta.scrollLeft = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function refresh() {
|
function refresh() {
|
||||||
// 높이는 색칠 결과에서 나오므로 다시 칠한 뒤에 맞춘다.
|
// 짧은 소스는 바로 칠해야 줄 번호·상자 크기가 즉시 따라온다.
|
||||||
|
if (ta.value.length < 50000) { repaint(); return; }
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
timer = setTimeout(function () { repaint(); resize(); }, 60);
|
timer = setTimeout(repaint, 80);
|
||||||
}
|
}
|
||||||
|
|
||||||
ta.addEventListener("input", function () { dirty = true; refresh(); });
|
ta.addEventListener("input", function () { dirty = true; refresh(); });
|
||||||
@@ -184,7 +192,6 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
repaint();
|
repaint();
|
||||||
resize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 편집기 조각을 새로 끼워 넣은 뒤 다시 연결 ──
|
// ── 편집기 조각을 새로 끼워 넣은 뒤 다시 연결 ──
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814f" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814g" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{% extends "erp_base.html" %}
|
{% extends "erp_base.html" %}
|
||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<link rel="stylesheet" href="/static/cafe24.css?v=20260814f" />
|
<link rel="stylesheet" href="/static/cafe24.css?v=20260814g" />
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|||||||
@@ -469,6 +469,45 @@ def test_format_is_idempotent():
|
|||||||
assert store.format_html(store.format_html(once)) == once
|
assert store.format_html(store.format_html(once)) == once
|
||||||
|
|
||||||
|
|
||||||
|
_REAL_DETAIL = """<div style="width: 1000px; margin: 0 auto;">
|
||||||
|
|
||||||
|
<img src="../img/promo/dadamam_detail1.jpg">
|
||||||
|
<img src="../img/promo/dadamam_detail2.jpg">
|
||||||
|
|
||||||
|
<!-- 대파_타임랩스----------------><img contenteditable="false" src="../img/gif/NEW_1.gif">
|
||||||
|
<img contenteditable="false" src="../img/2+1/2+1_02.jpg">
|
||||||
|
</div>"""
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_indents_every_line_of_a_run():
|
||||||
|
"""원문 줄바꿈을 살리고 **모든 줄**을 들여쓴다.
|
||||||
|
|
||||||
|
예전에는 첫 줄만 들여쓰고 나머지가 1열에 붙어 나왔다.
|
||||||
|
"""
|
||||||
|
lines = store.format_html(_REAL_DETAIL).split("\n")
|
||||||
|
img_lines = [line for line in lines if "<img" in line]
|
||||||
|
assert len(img_lines) == 4, img_lines
|
||||||
|
assert all(line.startswith(" <") for line in img_lines), img_lines
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_keeps_comment_with_its_element():
|
||||||
|
"""`<!-- 라벨 --><img>` 는 붙여둔다 — 나누면 라벨과 대상이 떨어진다."""
|
||||||
|
out = store.format_html(_REAL_DETAIL)
|
||||||
|
assert "<!-- 대파_타임랩스----------------><img contenteditable=" in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_keeps_single_blank_line():
|
||||||
|
"""구획용 빈 줄은 한 줄까지 유지한다(여러 줄은 하나로)."""
|
||||||
|
out = store.format_html("<div>\n\n\n<img src=\"a.gif\">\n\n\n<img src=\"b.gif\">\n</div>")
|
||||||
|
assert "\n\n" in out
|
||||||
|
assert "\n\n\n" not in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_format_real_detail_is_idempotent():
|
||||||
|
once = store.format_html(_REAL_DETAIL)
|
||||||
|
assert store.format_html(once) == once
|
||||||
|
|
||||||
|
|
||||||
def test_format_collapses_short_blocks():
|
def test_format_collapses_short_blocks():
|
||||||
assert store.format_html("<td>1</td>") == "<td>1</td>"
|
assert store.format_html("<td>1</td>") == "<td>1</td>"
|
||||||
# 길면 나눈다.
|
# 길면 나눈다.
|
||||||
|
|||||||
+48
-11
@@ -264,44 +264,81 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
min-height: 340px;
|
min-height: 340px;
|
||||||
overflow: auto;
|
overflow: auto; /* 가로·세로 스크롤을 여기서 담당 */
|
||||||
border: 1px solid var(--color-subtle-ash, #e5e5e5);
|
border: 1px solid var(--color-subtle-ash, #e5e5e5);
|
||||||
border-radius: var(--r-lg, 10px);
|
border-radius: var(--r-lg, 10px);
|
||||||
background: var(--color-canvas-white, #fff);
|
background: var(--color-canvas-white, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cf24-code-rows {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
min-width: max-content; /* 가장 긴 줄에 맞춰 넓어진다(줄바꿈 없음) */
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 줄 번호 — 가로로 스크롤해도 왼쪽에 붙어 있게 sticky */
|
||||||
|
.cf24-gutter {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
padding: var(--sp-12, 12px) var(--sp-8, 8px);
|
||||||
|
text-align: right;
|
||||||
|
background: var(--color-ghost-gray, #f6f8fa);
|
||||||
|
border-right: 1px solid var(--color-subtle-ash, #e5e5e5);
|
||||||
|
color: #8c959f;
|
||||||
|
user-select: none;
|
||||||
|
white-space: pre;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cf24-code-body {
|
||||||
|
position: relative; /* textarea 의 기준 박스 — 크기는 <pre> 가 정한다 */
|
||||||
|
flex: 1 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 두 층의 글자가 정확히 겹치려면 아래 속성이 전부 같아야 한다.
|
||||||
|
줄 번호(.cf24-gutter)도 같은 글꼴 지표를 써야 줄이 맞는다. */
|
||||||
|
.cf24-gutter,
|
||||||
.cf24-code-hl,
|
.cf24-code-hl,
|
||||||
.cf24-code-input {
|
.cf24-code-input {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: var(--sp-12, 12px);
|
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
font-family: var(--font-geist-mono, ui-monospace, "Consolas", monospace);
|
font-family: var(--font-geist-mono, ui-monospace, "Consolas", monospace);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
white-space: pre-wrap;
|
|
||||||
overflow-wrap: break-word;
|
|
||||||
tab-size: 2;
|
tab-size: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cf24-code-hl,
|
||||||
|
.cf24-code-input {
|
||||||
|
padding: var(--sp-12, 12px);
|
||||||
|
white-space: pre; /* 줄바꿈하지 않는다 — 줄 번호가 어긋나지 않게 */
|
||||||
|
overflow-wrap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* <pre> 가 흐름에 남아 본문 칸의 크기를 결정한다. */
|
||||||
.cf24-code-hl {
|
.cf24-code-hl {
|
||||||
position: absolute;
|
width: max-content;
|
||||||
top: 0;
|
min-width: 100%;
|
||||||
left: 0;
|
|
||||||
min-height: 100%;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
color: var(--color-rich-black, #0a0a0a);
|
color: var(--color-rich-black, #0a0a0a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* textarea 는 그 위를 정확히 덮는다(크기 계산을 JS 로 하지 않아도 어긋나지 않음). */
|
||||||
.cf24-code-input {
|
.cf24-code-input {
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
display: block;
|
display: block;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: transparent;
|
color: transparent;
|
||||||
caret-color: var(--color-rich-black, #0a0a0a);
|
caret-color: var(--color-rich-black, #0a0a0a);
|
||||||
resize: none;
|
resize: none;
|
||||||
overflow: hidden; /* 높이를 내용에 맞추고 스크롤은 .cf24-code 가 담당 */
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cf24-code-input:focus {
|
.cf24-code-input:focus {
|
||||||
|
|||||||
+15
-6
@@ -166,12 +166,15 @@ cafe24_oauth_tokens 저장
|
|||||||
**문법 강조** — 색칠된 `<pre>` 위에 **투명한 `<textarea>`** 를 정확히 겹쳐 놓는
|
**문법 강조** — 색칠된 `<pre>` 위에 **투명한 `<textarea>`** 를 정확히 겹쳐 놓는
|
||||||
방식이다. 외부 라이브러리를 쓰지 않는다(자체 호스팅 원칙).
|
방식이다. 외부 라이브러리를 쓰지 않는다(자체 호스팅 원칙).
|
||||||
|
|
||||||
- 두 층의 **폰트·글자크기·줄높이·padding·`white-space`·`overflow-wrap`·`tab-size`
|
- 두 층의 **폰트·글자크기·줄높이·padding·`white-space`·`tab-size` 가 완전히 같아야**
|
||||||
가 완전히 같아야** 글자가 어긋나지 않는다. `cafe24.css` 의
|
글자가 어긋나지 않는다. 줄 번호 칸(`.cf24-gutter`)도 같은 글꼴 지표를 써야 줄이
|
||||||
`.cf24-code-hl, .cf24-code-input` 규칙을 항상 함께 수정할 것.
|
맞는다. `cafe24.css` 의 세 선택자를 항상 함께 수정할 것.
|
||||||
- 높이는 **`<pre>` 의 `scrollHeight` 를 기준**으로 textarea 에 지정한다.
|
- **크기는 `<pre>` 가 정하고 textarea 가 `inset:0` 으로 그 위를 덮는다.** JS 로 높이를
|
||||||
textarea 의 `scrollHeight` 를 쓰면 브라우저가 한두 줄 더 잡아 두 층이 어긋난다
|
계산하지 않는다(textarea 의 `scrollHeight` 는 브라우저가 한두 줄 더 잡아 어긋난다 —
|
||||||
(실측 830 vs 792). 스크롤은 바깥 `.cf24-code` 가 담당한다.
|
실측 830 vs 792). 스크롤은 바깥 `.cf24-code` 가 담당한다.
|
||||||
|
- **줄바꿈하지 않고 가로로 스크롤한다**(`white-space: pre` + `wrap="off"`).
|
||||||
|
줄바꿈을 허용하면 한 논리 줄이 여러 행이 되어 줄 번호가 맞지 않는다.
|
||||||
|
- 줄 번호 칸은 `position: sticky; left: 0` 이라 가로로 스크롤해도 왼쪽에 남는다.
|
||||||
- 20만 자를 넘으면 강조를 끄고 평문으로 보여준다(타이핑마다 재색칠하면 느려짐).
|
- 20만 자를 넘으면 강조를 끄고 평문으로 보여준다(타이핑마다 재색칠하면 느려짐).
|
||||||
- 색: 태그 초록 / 속성이름 갈색 / 속성값 남색 / 주석 회색 기울임 / 기호 회색.
|
- 색: 태그 초록 / 속성이름 갈색 / 속성값 남색 / 주석 회색 기울임 / 기호 회색.
|
||||||
|
|
||||||
@@ -181,6 +184,12 @@ cafe24_oauth_tokens 저장
|
|||||||
- 줄을 나누는 것은 **블록 요소 경계에서만** 한다. HTML 에서 공백은 의미가 있어서
|
- 줄을 나누는 것은 **블록 요소 경계에서만** 한다. HTML 에서 공백은 의미가 있어서
|
||||||
인라인 요소 사이에 줄바꿈을 넣으면 화면에 공백이 생긴다(이미지 사이가 벌어지는
|
인라인 요소 사이에 줄바꿈을 넣으면 화면에 공백이 생긴다(이미지 사이가 벌어지는
|
||||||
고전적인 사고). `img`·`br`·`span`·`a` 는 블록 목록에서 **의도적으로 제외**했다.
|
고전적인 사고). `img`·`br`·`span`·`a` 는 블록 목록에서 **의도적으로 제외**했다.
|
||||||
|
- **원문에 이미 있던 줄바꿈은 살리고, 각 줄을 현재 깊이로 들여쓴다.** 상세페이지는
|
||||||
|
`<img>` 를 한 줄에 하나씩 적어두는 경우가 많고 그 모양이 저자의 의도다. 줄 앞
|
||||||
|
공백은 렌더링에 영향이 없으므로 들여쓰기는 안전하다.
|
||||||
|
- 주석은 줄을 강제로 나누지 않는다. `<!-- 대파_타임랩스 --><img ...>` 처럼 바로 뒤
|
||||||
|
요소를 설명하는 주석이 많아, 나누면 라벨과 대상이 떨어져 오히려 읽기 나빠진다.
|
||||||
|
- 구획용 **빈 줄은 한 줄까지 유지**한다(여러 줄은 하나로 줄인다).
|
||||||
- `<style>`·`<script>`·`<pre>`·`<textarea>` 안쪽은 한 글자도 건드리지 않는다.
|
- `<style>`·`<script>`·`<pre>`·`<textarea>` 안쪽은 한 글자도 건드리지 않는다.
|
||||||
- 내용이 한 줄뿐인 짧은 블록은 다시 한 줄로 합친다(`<td>1</td>`).
|
- 내용이 한 줄뿐인 짧은 블록은 다시 한 줄로 합친다(`<td>1</td>`).
|
||||||
- **멱등**이다 — 편집하지 않고 다시 적용해도 저장값이 계속 바뀌지 않는다(테스트로 고정).
|
- **멱등**이다 — 편집하지 않고 다시 적용해도 저장값이 계속 바뀌지 않는다(테스트로 고정).
|
||||||
|
|||||||
Reference in New Issue
Block a user