refactor: migrate examples to the canonical Ebox DSL

This commit is contained in:
Kinneyzhang 2026-08-27 12:14:51 +08:00
parent f571c89d70
commit 94bac1c7c7
11 changed files with 1976 additions and 1379 deletions

View File

@ -41,7 +41,7 @@
- Imagery/iconography: Text-only. - Imagery/iconography: Text-only.
## Components ## Components
- Existing components to reuse: `ebox-create`, `ebox-column`, `ebox-row`, `ebox-flex`, `ebox-grid`, and `ebox-spacer`. - Author forms to reuse: String, `text`, `box`, `row`, `column`, `flex`, and `grid`. Empty `box` nodes express spacing directly without another public form.
- New/changed components: Header band, property section bands, track-function cards, implicit-track/flow cards, gap/placement cards, alignment cards, and public-contract footer. - New/changed components: Header band, property section bands, track-function cards, implicit-track/flow cards, gap/placement cards, alignment cards, and public-contract footer.
- Variants and states: Static semantic color families only. - Variants and states: Static semantic color families only.
- Token/component ownership: Palette values and example composition live in `.ebox` fixtures; the runner owns only parsing and rendering. The migrated references remain data files, so adding a layout does not add a fixture-specific Elisp branch. - Token/component ownership: Palette values and example composition live in `.ebox` fixtures; the runner owns only parsing and rendering. The migrated references remain data files, so adding a layout does not add a fixture-specific Elisp branch.

View File

@ -41,7 +41,7 @@
- 图像与图标:纯文本。 - 图像与图标:纯文本。
## 组件 ## 组件
- 复用的现有组件:`ebox-create`、`ebox-column`、`ebox-row`、`ebox-flex`、`ebox-grid` 和 `ebox-spacer` - 复用的作者 formString、`text`、`box`、`row`、`column`、`flex` 和 `grid`。空 `box` 直接表达间距,不再引入另一个公共 form
- 新增/调整的组件:标题带、能力分区标题带、固定/分数轨道卡片、放置卡片和公开契约页脚。 - 新增/调整的组件:标题带、能力分区标题带、固定/分数轨道卡片、放置卡片和公开契约页脚。
- 变体与状态:仅使用静态语义色组。 - 变体与状态:仅使用静态语义色组。
- Token/组件归属:配色值和示例组合放在 `.ebox` fixture 中;运行器只负责解析和渲染。迁移来的参考仍然是数据文件,新增布局不需要在 Elisp 中增加针对 fixture 的分支。 - Token/组件归属:配色值和示例组合放在 `.ebox` fixture 中;运行器只负责解析和渲染。迁移来的参考仍然是数据文件,新增布局不需要在 Elisp 中增加针对 fixture 的分支。

View File

@ -167,7 +167,9 @@ CANVAS-BACKGROUND fills display space outside generated root lines."
(prog1 (prog1
(progn (progn
(ebox-playground--apply-preview-display-settings (ebox-playground--apply-preview-display-settings
(and (consp node) (plist-get node :bgcolor))) (and (consp node)
(ebox-style-node-specified-value
node :background-color)))
(ebox-render-to-buffer buffer node)) (ebox-render-to-buffer buffer node))
(setq published-p t)) (setq published-p t))
(unless published-p (unless published-p

View File

@ -1,7 +1,3 @@
(box :content "Playground file" (box :box-sizing 'border-box :padding '(1 (12)) :border t
:box-sizing 'border-box :border-color "#2563EB" :bgcolor "#EFF6FF" :color "#1E3A8A"
:padding '(1 (12)) "Playground file")
:border t
:border-color "#2563EB"
:bgcolor "#EFF6FF"
:color "#1E3A8A")

View File

@ -1,41 +1,29 @@
(box :layout 'column (column
(box :content "Ebox comprehensive DSL showcase\nThe outer sections omit :width so Playground viewport sizing drives the layout." (box :padding '(1 (16)) :border "#334155" :bgcolor "#F8FAFC" :color
:padding '(1 (16)) :border "#334155" "#0F172A" :text-align 'center
:bgcolor "#F8FAFC" :color "#0F172A" :text-align 'center) "Ebox comprehensive DSL showcase\nThe outer sections omit :width so Playground viewport sizing drives the layout.")
(box :width '(viewport) :height 1 :bgcolor "#F8FAFC") (box :width '(viewport) :height 1 :bgcolor "#F8FAFC")
(box :padding '(0 (14)) :border "#64748B" :bgcolor "#F8FAFC" :color
(box :content "Width rule in this file\nContainer sections use auto width. Fixed widths remain only where the example demonstrates exact item sizing, basis, min-width, or max-width." "#111827" :wrap-mode 'word
:padding '(0 (14)) :border "#64748B" "Width rule in this file\nContainer sections use auto width. Fixed widths remain only where the example demonstrates exact item sizing, basis, min-width, or max-width.")
:bgcolor "#F8FAFC" :color "#111827" :wrap-mode 'word)
(box :width '(viewport) :height 1 :bgcolor "#F8FAFC") (box :width '(viewport) :height 1 :bgcolor "#F8FAFC")
(flex :margin '(1 0 0 1) :padding '(1 (8)) :border "#475569" :bgcolor
(box :layout 'flex "#F8FAFC" :flex-flow '(row wrap) :justify-content
:margin '(1 0 0 1) :padding '(1 (8)) 'space-between :align-items 'center :align-content 'flex-start
:border "#475569" :bgcolor "#F8FAFC" :row-gap 1 :column-gap '(12)
:flex-flow '(row wrap) (box :order -1 :flex-basis '(270) :align-self 'flex-start
:justify-content 'space-between :align-items 'center :width '(270) :padding '(0 (8)) :border "#BE185D"
:align-content 'flex-start :row-gap 1 :column-gap '(12) :bgcolor "#FCE7F3" :color "#831843" :wrap-mode 'word
(item "Box order -1\n:flex-basis 270\nalign-self start")
:order -1 :flex-basis '(270) :align-self 'flex-start (box :flex '(1 1 (220)) :width '(220) :height 4 :padding
(box :content "item order -1\n:flex-basis 270\nalign-self start" '(0 (8)) :border "#1D4ED8" :bgcolor "#DBEAFE" :color
:width '(270) :padding '(0 (8)) "#111827" :wrap-mode 'word
:border "#BE185D" :bgcolor "#FCE7F3" :color "#831843" "Box :flex (1 1 220)\ngrow shrink basis")
:wrap-mode 'word)) (box :flex-grow 2 :flex-shrink 1 :flex-basis '(240) :align-self
(box :flex '(1 1 (220)) 'center :width '(240) :height 4 :padding '(0 (8)) :border
:content "Box :flex (1 1 220)\ngrow shrink basis" "#B45309" :bgcolor "#FEF3C7" :color "#78350F" :wrap-mode
:width '(220) :height 4 'word "Box grow 2\nshrink 1\nbasis 240")
:padding '(0 (8)) :border "#1D4ED8" (box :flex-shrink 3 :flex-basis '(190) :align-self 'flex-end
:bgcolor "#DBEAFE" :color "#111827" :wrap-mode 'word) :width '(190) :height 4 :padding '(0 (8)) :border
(box :flex-grow 2 :flex-shrink 1 :flex-basis '(240) "#15803D" :bgcolor "#DCFCE7" :color "#14532D" :wrap-mode
:align-self 'center 'word "Box shrink 3\nbasis 190\nalign-self flex-end")))
:content "Box grow 2\nshrink 1\nbasis 240"
:width '(240) :height 4 :padding '(0 (8))
:border "#B45309" :bgcolor "#FEF3C7" :color "#78350F"
:wrap-mode 'word)
(box :flex-shrink 3 :flex-basis '(190) :align-self 'flex-end
:content "Box shrink 3\nbasis 190\nalign-self flex-end"
:width '(190) :height 4 :padding '(0 (8))
:border "#15803D" :bgcolor "#DCFCE7" :color "#14532D"
:wrap-mode 'word)))

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,98 +1,61 @@
(box :layout 'column (column
(box :content "Ebox Playground\nPixel-precise layout, composed only from public nodes." (box :width '(720) :padding '(1 (18)) :border "#8F432F" :color
:width '(720) "#252A2E" :bgcolor "#FFFDF8" :font-weight 'bold :text-align 'center
:padding '(1 (18)) "Ebox Playground\nPixel-precise layout, composed only from public nodes.")
:border "#8F432F"
:color "#252A2E"
:bgcolor "#FFFDF8"
:face 'bold
:text-align 'center)
(box :width '(720) :height 1 :bgcolor "#FAF7F0") (box :width '(720) :height 1 :bgcolor "#FAF7F0")
(box :content "01 FIXED + FRACTIONAL\nA definite Grid width divides into one exact track and one flexible track." (box :width '(720) :padding '(0 (16)) :border "#C97252"
:width '(720) :border-bottom 'none :color "#252A2E" :bgcolor "#F1D4C9"
:padding '(0 (16)) :wrap-mode 'word
:border "#C97252" "01 FIXED + FRACTIONAL\nA definite Grid width divides into one exact track and one flexible track.")
:border-bottom 'none (grid :width '(720) :grid-template-columns '((220) 1fr) :gap
:color "#252A2E" '(1 (12)) :border "#C97252" :bgcolor "#FFF9F5"
:bgcolor "#F1D4C9" (box :width '(220) :padding '(1 0) :text-align 'center :color
:wrap-mode 'word) "#252A2E" :bgcolor "#F1D4C9"
(box :layout 'grid "FIXED TRACK\n220 px stays exact")
:width '(720) (box :width '(486) :padding '(1 0) :text-align 'center :color
:grid-template-columns '((220) 1fr) "#252A2E" :bgcolor "#F9E8E0"
:gap '(1 (12)) "FRACTIONAL TRACK\n1fr receives the remaining width")
:border "#C97252" (box :grid-column '(1 :span 2) :width '(718) :padding '(1 0)
:bgcolor "#FFF9F5" :color "#663322" :bgcolor "#FFFDF8" :text-align 'center
(box :content "FIXED TRACK\n220 px stays exact" :wrap-mode 'word
:width '(220) :padding '(1 0) :text-align 'center "The Grid owns one definite width; its tracks divide that space without competing viewport calculations."))
:color "#252A2E" :bgcolor "#F1D4C9")
(box :content "FRACTIONAL TRACK\n1fr receives the remaining width"
:width '(486) :padding '(1 0) :text-align 'center
:color "#252A2E" :bgcolor "#F9E8E0")
(box :content "The Grid owns one definite width; its tracks divide that space without competing viewport calculations."
:grid-column '(1 :span 2) :width '(718) :padding '(1 0)
:color "#663322" :bgcolor "#FFFDF8"
:text-align 'center :wrap-mode 'word))
(box :width '(720) :height 1 :bgcolor "#FAF7F0") (box :width '(720) :height 1 :bgcolor "#FAF7F0")
(box :content "02 EXPLICIT PLACEMENT\nSpan columns, target cells, and leave the remaining nodes to auto placement." (box :width '(720) :padding '(0 (16)) :border "#6D8A73"
:width '(720) :border-bottom 'none :color "#252A2E" :bgcolor "#DCEBDD"
:padding '(0 (16)) :wrap-mode 'word
:border "#6D8A73" "02 EXPLICIT PLACEMENT\nSpan columns, target cells, and leave the remaining nodes to auto placement.")
:border-bottom 'none (grid :width '(720) :grid-template-columns '((210) (210) 1fr) :gap
:color "#252A2E" '(1 (10)) :border "#6D8A73" :bgcolor "#F7FBF7"
:bgcolor "#DCEBDD" (box :grid-column '(1 :span 2) :width '(430) :padding '(1 0)
:wrap-mode 'word) :text-align 'center :color "#24422D" :bgcolor "#DCEBDD"
(box :layout 'grid "SPAN 1 → 3")
:width '(720) (box :grid-column 3 :grid-row 1 :width '(278) :padding '(1 0)
:grid-template-columns '((210) (210) 1fr) :text-align 'center :color "#24422D" :bgcolor "#C8DDCB"
:gap '(1 (10)) "COLUMN 3")
:border "#6D8A73" (box :grid-column 1 :grid-row 2 :width '(210) :padding '(1 0)
:bgcolor "#F7FBF7" :text-align 'center :color "#24422D" :bgcolor "#EDF5EE"
(box :content "SPAN 1 → 3" :grid-column '(1 :span 2) "ROW 2 / COL 1")
:width '(430) :padding '(1 0) :text-align 'center (box :width '(210) :padding '(1 0) :text-align 'center :color
:color "#24422D" :bgcolor "#DCEBDD") "#24422D" :bgcolor "#E3F0E5" "AUTO PLACED")
(box :content "COLUMN 3" :grid-column 3 :grid-row 1 (box :width '(278) :padding '(1 0) :text-align 'center :color
:width '(278) :padding '(1 0) :text-align 'center "#24422D" :bgcolor "#D4E6D7" "STABLE CELLS"))
:color "#24422D" :bgcolor "#C8DDCB")
(box :content "ROW 2 / COL 1" :grid-column 1 :grid-row 2
:width '(210) :padding '(1 0) :text-align 'center
:color "#24422D" :bgcolor "#EDF5EE")
(box :content "AUTO PLACED" :width '(210) :padding '(1 0)
:text-align 'center :color "#24422D" :bgcolor "#E3F0E5")
(box :content "STABLE CELLS" :width '(278) :padding '(1 0)
:text-align 'center :color "#24422D" :bgcolor "#D4E6D7"))
(box :width '(720) :height 1 :bgcolor "#FAF7F0") (box :width '(720) :height 1 :bgcolor "#FAF7F0")
(box :content "03 PUBLIC COMPOSITION\nColumn, Flex, and Grid share the same public node contract." (box :width '(720) :padding '(0 (16)) :border "#4E7890"
:width '(720) :border-bottom 'none :color "#252A2E" :bgcolor "#D9EAF2"
:padding '(0 (16)) :wrap-mode 'word
:border "#4E7890" "03 PUBLIC COMPOSITION\nColumn, Flex, and Grid share the same public node contract.")
:border-bottom 'none (flex :width '(720) :flex-flow '(row wrap) :gap '(1 (12)) :padding
:color "#252A2E" '(1 (12)) :border "#4E7890" :bgcolor "#F7FAFC"
:bgcolor "#D9EAF2" (box :flex-grow 1 :flex-shrink 1 :flex-basis '(200) :min-width
:wrap-mode 'word) '(180) :padding '(1 (12)) :color "#193846" :bgcolor
(box :layout 'flex "#D9EAF2" "COLUMN\nvertical composition")
:width '(720) (box :flex-grow 1 :flex-shrink 1 :flex-basis '(200) :min-width
:flex-flow '(row wrap) '(180) :padding '(1 (12)) :color "#3F3655" :bgcolor
:gap '(1 (12)) "#E7E2F1" "FLEX\nwrap and distribute")
:padding '(1 (12)) (box :flex-grow 1 :flex-shrink 1 :flex-basis '(200) :min-width
:border "#4E7890" '(180) :padding '(1 (12)) :color "#6B3020" :bgcolor
:bgcolor "#F7FAFC" "#F1D4C9" "GRID\nplace in two dimensions"))
(box :content "COLUMN\nvertical composition"
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
:min-width '(180) :padding '(1 (12))
:color "#193846" :bgcolor "#D9EAF2")
(box :content "FLEX\nwrap and distribute"
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
:min-width '(180) :padding '(1 (12))
:color "#3F3655" :bgcolor "#E7E2F1")
(box :content "GRID\nplace in two dimensions"
:flex-grow 1 :flex-shrink 1 :flex-basis '(200)
:min-width '(180) :padding '(1 (12))
:color "#6B3020" :bgcolor "#F1D4C9"))
(box :width '(720) :height 1 :bgcolor "#FAF7F0") (box :width '(720) :height 1 :bgcolor "#FAF7F0")
(box :content "PUBLIC CONTRACT ebox-create · ebox-column · ebox-flex · ebox-grid · ebox-spacer" (box :width '(720) :padding '(0 (16)) :border "#8D887F" :color
:width '(720) "#4D5651" :bgcolor "#EEEAE2" :wrap-mode 'word
:padding '(0 (16)) "AUTHOR FORMS String · text · box · row · column · flex · grid"))
:border "#8D887F"
:color "#4D5651"
:bgcolor "#EEEAE2"
:wrap-mode 'word))

View File

@ -1,65 +1,57 @@
(box :layout 'column (column :margin-left 1 :margin-top 1 :padding '(1 2 1 2) :bgcolor
:margin-left 1 :margin-top 1 :padding '(1 2 1 2) :bgcolor "linen" "linen"
(box :content "Ebox Responsive Layout\nDrag the preview window: sections stretch to the current viewport, cards reflow, and text wraps without changing the window layout." (box :padding '(1 (18)) :border "#334155" :text-align 'center
:padding '(1 (18)) :border "#334155" :text-align 'center :color "#000" :bgcolor "ghost white"
:color "#000" :bgcolor "ghost white") "Ebox Responsive Layout\nDrag the preview window: sections stretch to the current viewport, cards reflow, and text wraps without changing the window layout.")
(box :height 1) (box :height 1)
(box :padding '(0 (16)) :border "#0F766E" :bgcolor "#F0FDFA"
(box :content "No explicit viewport width here\nThis page mostly omits :width. The Playground binds the current preview width while rendering, so auto width behaves like a responsive container." :color "#134E4A" :wrap-mode 'word
:padding '(0 (16)) :border "#0F766E" "No explicit viewport width here\nThis page mostly omits :width. The Playground binds the current preview width while rendering, so auto width behaves like a responsive container.")
:bgcolor "#F0FDFA" :color "#134E4A" :wrap-mode 'word) (box :height 1)
(box :height 1) (box :padding '(0 (16)) :border "#EA580C" :border-bottom 'none
:bgcolor "#FFF7ED" :color "#431407" :wrap-mode 'word
(box :content "Adaptive toolbar\nButtons below keep stable sizes. The row packs as many as fit and then wraps." "Adaptive toolbar\nButtons below keep stable sizes. The row packs as many as fit and then wraps.")
:padding '(0 (16)) :border "#EA580C" :border-bottom 'none (flex :flex-flow '(row wrap) :column-gap '(8) :row-gap 1
:bgcolor "#FFF7ED" :color "#431407" :wrap-mode 'word) :padding '(1 (8)) :border "#FDBA74" :bgcolor "#FFFBF5"
(box :layout 'flex :flex-flow '(row wrap) :column-gap '(8) :row-gap 1 (box :width '(96) :padding '(0 (8)) :border "#EA580C"
:padding '(1 (8)) :border "#FDBA74" :bgcolor "#FFFBF5" :bgcolor "#FED7AA" :color "#111827" :text-align
(box :content "Render" :width '(96) :padding '(0 (8)) 'center "Render")
:border "#EA580C" :bgcolor "#FED7AA" :color "#111827" (box :width '(88) :padding '(0 (8)) :border "#D97706"
:text-align 'center) :bgcolor "#FEF3C7" :color "#111827" :text-align
(box :content "Save" :width '(88) :padding '(0 (8)) 'center "Save")
:border "#D97706" :bgcolor "#FEF3C7" :color "#111827" (box :width '(96) :padding '(0 (8)) :border "#16A34A"
:text-align 'center) :bgcolor "#DCFCE7" :color "#14532D" :text-align
(box :content "Format" :width '(96) :padding '(0 (8)) 'center "Format")
:border "#16A34A" :bgcolor "#DCFCE7" :color "#14532D" (box :width '(104) :padding '(0 (8)) :border "#0284C7"
:text-align 'center) :bgcolor "#E0F2FE" :color "#0C4A6E" :text-align
(box :content "Sample" :width '(104) :padding '(0 (8)) 'center "Sample")
:border "#0284C7" :bgcolor "#E0F2FE" :color "#0C4A6E" (box :width '(88) :padding '(0 (8)) :border "#9333EA"
:text-align 'center) :bgcolor "#F3E8FF" :color "#3B0764" :text-align
(box :content "Write" :width '(88) :padding '(0 (8)) 'center "Write"))
:border "#9333EA" :bgcolor "#F3E8FF" :color "#3B0764" (box :height 1)
:text-align 'center)) (box :padding '(0 (16)) :border "#2563EB" :border-bottom 'none
(box :height 1) :bgcolor "#EFF6FF" :color "#1E3A8A" :wrap-mode 'word
"Responsive cards\nEach card starts from a flex-basis, grows when space is available, and wraps to new rows when the preview becomes narrow.")
(box :content "Responsive cards\nEach card starts from a flex-basis, grows when space is available, and wraps to new rows when the preview becomes narrow." (flex :flex-flow '(row wrap) :column-gap '(12) :row-gap 1
:padding '(0 (16)) :border "#2563EB" :border-bottom 'none :padding '(1 (8)) :border "#93C5FD" :bgcolor "#F8FBFF"
:bgcolor "#EFF6FF" :color "#1E3A8A" :wrap-mode 'word) :justify-content 'center
(box :layout 'flex :flex-flow '(row wrap) :column-gap '(12) :row-gap 1 (box :flex '(1 1 (210)) :min-width '(180) :max-width
:padding '(1 (8)) :border "#93C5FD" :bgcolor "#F8FBFF" '(280) :padding '(0 (10)) :bgcolor "#DBEAFE" :color
:justify-content 'center "#111827" :wrap-mode 'word
(box :flex '(1 1 (210)) "Overview\nThe row uses the current preview width automatically.")
:content "Overview\nThe row uses the current preview width automatically." (box :flex '(1 1 (210)) :min-width '(180) :max-width
:min-width '(180) :max-width '(280) '(300) :padding '(0 (10)) :bgcolor "#E0E7FF" :color
:padding '(0 (10)) ;; :border "#2563EB" "#111827" :wrap-mode 'word
:bgcolor "#DBEAFE" :color "#111827" :wrap-mode 'word) "Editor\nDrag slowly or quickly; resize rendering follows the viewport.")
(box :flex '(1 1 (210)) (box :flex '(1 1 (210)) :min-width '(180) :max-width
:content "Editor\nDrag slowly or quickly; resize rendering follows the viewport." '(320) :padding '(0 (10)) :bgcolor "#EDE9FE" :color
:min-width '(180) :max-width '(300) "#111827" :wrap-mode 'word
:padding '(0 (10)) ;; :border "#4F46E5" "Preview\nThe preview updates from the current viewport width.")
:bgcolor "#E0E7FF" :color "#111827" :wrap-mode 'word) (box :flex '(1 1 (210)) :min-width '(180) :max-width
(box :flex '(1 1 (210)) '(280) :padding '(0 (10)) :bgcolor "#CFFAFE" :color
:content "Preview\nThe preview updates from the current viewport width." "#164E63" :wrap-mode 'word
:min-width '(180) :max-width '(320) "Flow\nCards keep readable widths instead of overflowing."))
:padding '(0 (10)) ;; :border "#7C3AED" (box :height 1)
:bgcolor "#EDE9FE" :color "#111827" :wrap-mode 'word) (box :padding '(0 (16)) :border "#475569" :bgcolor "#F8FAFC"
(box :flex '(1 1 (210)) :color "#0F172A" :wrap-mode 'word
:content "Flow\nCards keep readable widths instead of overflowing." "What to watch\nNarrow preview: cards stack and text wraps. Wide preview: cards share a row and expand up to max-width. The source/preview split is preserved."))
:min-width '(180) :max-width '(280)
:padding '(0 (10)) ;; :border "#0891B2"
:bgcolor "#CFFAFE" :color "#164E63" :wrap-mode 'word))
(box :height 1)
(box :content "What to watch\nNarrow preview: cards stack and text wraps. Wide preview: cards share a row and expand up to max-width. The source/preview split is preserved."
:padding '(0 (16)) :border "#475569"
:bgcolor "#F8FAFC" :color "#0F172A" :wrap-mode 'word))

View File

@ -120,7 +120,7 @@ concurrent cache warming CPU work."
(completed-without-error nil) (completed-without-error nil)
(ebox-playground-flex-resize-evaluator--output-lines nil) (ebox-playground-flex-resize-evaluator--output-lines nil)
(checks nil)) (checks nil))
(condition-case error-data (condition-case-unless-debug error-data
(progn (progn
(delete-other-windows) (delete-other-windows)
(setq source (setq source

View File

@ -33,6 +33,13 @@
(ebox-playground-test--forms-named name child)) (ebox-playground-test--forms-named name child))
form)))) form))))
(defun ebox-playground-test--form-children (form)
"Return canonical author children from DSL FORM."
(let ((items (cdr form)))
(while (keywordp (car items))
(setq items (cddr items)))
items))
(defun ebox-playground-test--line-widths (text) (defun ebox-playground-test--line-widths (text)
"Return rendered pixel widths for every line in TEXT." "Return rendered pixel widths for every line in TEXT."
(mapcar #'ebox-string-pixel-width (mapcar #'ebox-string-pixel-width
@ -115,15 +122,17 @@
ebox-playground-directory)))) ebox-playground-directory))))
(dolist (box (ebox-playground-test--boxes form)) (dolist (box (ebox-playground-test--boxes form))
(let ((properties (cdr box))) (let ((properties (cdr box)))
(when (and (memq :content properties) (when (and (cl-some #'stringp
(ebox-playground-test--form-children box))
(memq :bgcolor properties)) (memq :bgcolor properties))
(should (memq :color properties)))))))) (should (memq :color properties))))))))
(ert-deftest ebox-playground-uses-only-public-layout-apis () (ert-deftest ebox-playground-uses-only-public-layout-apis ()
"The example should build and render through the public Ebox entry point." "The example should build and render through the public Ebox entry point."
(let* ((node (ebox-playground-view)) (let* ((source (ebox-playground--read-file ebox-playground-default-file))
(node (ebox-playground-view))
(rendered (substring-no-properties (ebox-render node)))) (rendered (substring-no-properties (ebox-render node))))
(should (eq (plist-get node :ebox-type) 'stack)) (should (eq (car source) 'column))
(dolist (label '("Ebox Playground" "FIXED + FRACTIONAL" (dolist (label '("Ebox Playground" "FIXED + FRACTIONAL"
"EXPLICIT PLACEMENT" "PUBLIC COMPOSITION")) "EXPLICIT PLACEMENT" "PUBLIC COMPOSITION"))
(should (string-match-p (regexp-quote label) rendered))))) (should (string-match-p (regexp-quote label) rendered)))))
@ -234,7 +243,7 @@
(lambda (&optional _frame) 8))) (lambda (&optional _frame) 8)))
(with-temp-buffer (with-temp-buffer
(setq buffer-file-name "/tmp/ebox-playground-viewport-render.ebox") (setq buffer-file-name "/tmp/ebox-playground-viewport-render.ebox")
(insert "(box :content \"viewport\" :width '(viewport) :height 1)") (insert "(box :width '(viewport) :height 1 \"viewport\")")
(ebox-dsl-mode) (ebox-dsl-mode)
(ebox-dsl-render) (ebox-dsl-render)
(with-current-buffer preview (with-current-buffer preview
@ -281,7 +290,7 @@
(with-temp-buffer (with-temp-buffer
(setq source (current-buffer) (setq source (current-buffer)
buffer-file-name "/tmp/ebox-playground-two-window.ebox") buffer-file-name "/tmp/ebox-playground-two-window.ebox")
(insert "(box :content \"two windows\" :width '(viewport) :height 1)") (insert "(box :width '(viewport) :height 1 \"two windows\")")
(ebox-dsl-mode) (ebox-dsl-mode)
(cl-letf (((symbol-function 'window-body-width) (cl-letf (((symbol-function 'window-body-width)
(lambda (&optional _window _pixelwise) 987)) (lambda (&optional _window _pixelwise) 987))
@ -313,7 +322,7 @@
(lambda (&optional _frame) 8))) (lambda (&optional _frame) 8)))
(with-temp-buffer (with-temp-buffer
(setq buffer-file-name "/tmp/ebox-playground-viewport-resize.ebox") (setq buffer-file-name "/tmp/ebox-playground-viewport-resize.ebox")
(insert "(box :content \"viewport\" :width '(viewport) :height 1)") (insert "(box :width '(viewport) :height 1 \"viewport\")")
(ebox-dsl-mode) (ebox-dsl-mode)
(ebox-dsl-render) (ebox-dsl-render)
(setq window-width 1234) (setq window-width 1234)
@ -417,7 +426,7 @@
(error "Injected preview render failure")))) (error "Injected preview render failure"))))
(should-error (should-error
(ebox-playground--render-to-preview-buffer (ebox-playground--render-to-preview-buffer
preview (ebox-create :content "rollback")))) preview (ebox-build '(box "rollback")))))
(with-current-buffer preview (with-current-buffer preview
(should-not truncate-lines) (should-not truncate-lines)
(should (equal face-remapping-alist (should (equal face-remapping-alist
@ -481,21 +490,21 @@
(let* ((ebox-viewport-width viewport) (let* ((ebox-viewport-width viewport)
(lines (ebox-string-lines (lines (ebox-string-lines
(ebox-render (ebox-playground-view path viewport)))) (ebox-render (ebox-playground-view path viewport))))
(spacer-lines (separator-lines
(cl-remove-if-not (cl-remove-if-not
(lambda (line) (lambda (line)
(equal (get-text-property 0 'display line) (equal (get-text-property 0 'display line)
`(space :width (,viewport)))) `(space :width (,viewport))))
lines))) lines)))
(should (= (length spacer-lines) 11)) (should (= (length separator-lines) 11))
(dolist (line spacer-lines) (dolist (line separator-lines)
(should (equal (get-text-property 0 'display line) (should (equal (get-text-property 0 'display line)
`(space :width (,viewport)))) `(space :width (,viewport))))
(should (= (ebox-string-pixel-width line) viewport)) (should (= (ebox-string-pixel-width line) viewport))
(should (equal (get-text-property 0 'face line) (should (equal (get-text-property 0 'face line)
'(:background "#FAF7F0")))))))) '(:background "#FAF7F0"))))))))
(ert-deftest ebox-playground-public-layout-gallery-fixed-spacers-paint-the-canvas () (ert-deftest ebox-playground-public-layout-gallery-fixed-separators-paint-the-canvas ()
"The default public gallery fixed separators should keep canvas coverage." "The default public gallery fixed separators should keep canvas coverage."
(let* ((path (expand-file-name "examples/public-layout-gallery.ebox" (let* ((path (expand-file-name "examples/public-layout-gallery.ebox"
ebox-playground-directory)) ebox-playground-directory))
@ -512,11 +521,11 @@
(ert-deftest ebox-playground-grid-reference-covers-public-properties () (ert-deftest ebox-playground-grid-reference-covers-public-properties ()
"The Grid reference should cover every public property and supported value form." "The Grid reference should cover every public property and supported value form."
(let ((source (with-temp-buffer (let ((source
(insert-file-contents (prin1-to-string
(expand-file-name "examples/grid-reference.ebox" (ebox-playground--read-file
ebox-playground-directory)) (expand-file-name "examples/grid-reference.ebox"
(buffer-string)))) ebox-playground-directory)))))
(dolist (property '(:grid-template-columns :grid-template-rows (dolist (property '(:grid-template-columns :grid-template-rows
:grid-auto-columns :grid-auto-rows :grid-auto-flow :grid-auto-columns :grid-auto-rows :grid-auto-flow
:grid-row-gap :grid-column-gap :gap :row-gap :grid-row-gap :grid-column-gap :gap :row-gap
@ -565,7 +574,7 @@
(unwind-protect (unwind-protect
(with-temp-buffer (with-temp-buffer
(setq buffer-file-name "/tmp/ebox-playground-render-test.ebox") (setq buffer-file-name "/tmp/ebox-playground-render-test.ebox")
(insert "(box :content \"Rendered from .ebox\" :width '(240))") (insert "(box :width '(240) \"Rendered from .ebox\")")
(ebox-dsl-mode) (ebox-dsl-mode)
(ebox-dsl-render) (ebox-dsl-render)
(should (eq (window-buffer (selected-window)) (current-buffer))) (should (eq (window-buffer (selected-window)) (current-buffer)))
@ -578,16 +587,19 @@
(ert-deftest ebox-playground-evaluates-property-expressions () (ert-deftest ebox-playground-evaluates-property-expressions ()
"Evaluate executable property values while preserving quoted constants." "Evaluate executable property values while preserving quoted constants."
(let* ((form (ebox-playground--evaluate-form (let* ((form (ebox-playground--evaluate-form
'(box :content "Dynamic property values" '(box :width (ebox-playground-test--dynamic-width)
:width (ebox-playground-test--dynamic-width)
:color (ebox-playground-test--dynamic-color) :color (ebox-playground-test--dynamic-color)
:padding '(0 (4)) :padding '(0 (4))
:text-align 'center))) :text-align 'center
"Dynamic property values")))
(node (ebox-build form))) (node (ebox-build form)))
(should (equal (plist-get node :width) 240)) (should (equal (ebox-style-node-specified-value node :width) '(240)))
(should (equal (plist-get node :color) "#123456")) (should (equal (ebox-style-node-specified-value node :color) "#123456"))
(should (equal (plist-get node :padding-left-pixel) 4)) (should (equal
(should (eq (plist-get node :text-align) 'center)))) (ebox-style-node-specified-value node :padding-left)
'(4)))
(should (eq (ebox-style-node-specified-value node :text-align)
'center))))
(provide 'ebox-playground-tests) (provide 'ebox-playground-tests)