Lunaya

Cách làm một giao diện

Một giao diện là một tệp JSON nhỏ: bảng màu, chất giấy, độ bo góc, và nếu muốn thì cả cách sắp các khối trên tờ lịch. Lấy một bộ trong kho giao diện, đổi thứ bạn muốn, rồi nhập lại vào app.

Bắt đầu từ một tệp đã chạy được

  1. Tải bất kỳ giao diện nào trong kho — tệp nguồn .json nằm cạnh gói .lunayatheme.
  2. Đổi id thành tên của bạn. Id trùng một giao diện dựng sẵn thì app bỏ qua im lặng — không lỗi, không giao diện mới. Chín id đã dùng: do-tet · gach-bong · giay-cu · giay-do · giay-moi · muc-dem · nuoc-luc · son-mai · truc-xanh.
  3. Đổi name và các màu. Giữ basedOn — nó kế thừa mọi trường bạn không viết.
  4. Đổi tên tệp thành <id-của-bạn>.lunayatheme rồi nhập: trong Lunaya, Cài đặt → Giao diện → Nhập theme từ file. JSON trần cũng được — app đọc bốn byte đầu và nhận cả hai dạng.

Bảng màu

Mười một token. Mọi màu trong app lấy từ một trong số đó, nên một giao diện không bao giờ khai một màu hai lần. Giá trị dạng #RRGGBB.

Token Dùng cho
paperMàu giấy — nền của cả tờ lịch
paperShadeNền phụ, sát màu giấy — ô đang chọn, dải chip
inkMực chính — số ngày, chữ thường
inkFaintMực nhạt — nhãn nhỏ, chú thích
vermilionMực son — tên ngày lễ, chủ nhật
sealDấu triện — viền ngày hôm nay
jadeMực lục — icon con giáp, chữ can chi
goldKim nhũ — điểm nhấn hiếm
dividerĐường kẻ phân cách
surfaceNền thẻ nổi trên giấy
onSurfaceChữ trên nền thẻ

Cổng tương phản

App đo tỉ số tương phản WCAG giữa ink và paper. Dưới 4.5 thì cảnh báo, dưới 3 thì cảnh báo mạnh hơn. Nó không bao giờ từ chối tệp — một giao diện khó đọc vẫn là lựa chọn của bạn, và cảnh báo nói rõ cặp màu nào có vấn đề.

Hai token dễ quên: inkFaint là màu các nhãn nhỏ, còn vermilion là màu tên ngày lễ và chủ nhật. Cả hai đều là chữ thật — đo chúng với paper nữa, đừng chỉ đo ink.

Chất giấy

Hạt giấy, vệt ố và mực lem do app vẽ lên trên màu giấy. Hoa văn được vẽ bằng mã chứ không phải ảnh, nên nó đi theo màu mực của bạn và nét ở mọi mật độ. Năm slug:

hoi-van · song-nuoc · tram-cham · mat-vai · hoa-thi

Vượt biên thì bị kẹp, không bị từ chối

Viết 5.0 vào chỗ khoảng chỉ tới 1.4 thì app dùng 1.4, chứ không bỏ tệp đi. Đó là cố ý: một con số lỡ vượt biên không nên làm mất cả giao diện. Khoảng đọc từ đặc tả:

Trườngminmax
typography.scale.dayNumber 0.71.4
typography.scale.body 0.71.4
texture.grainAlpha 00.25
texture.stainAlpha 00.25
texture.inkBleed 01
shapes.cornerRadius 024
shapes.borderWidth 04
shapes.dividerWidth 04
component.style.scale 0.71.4
component.style.opacity 0.21
texture.patternAlpha 00.12
texture.patternScale 0.52.5

Sáng và tối

Đặt isDark để nói bảng màu của bạn thuộc loại nào. Một giao diện sáng có thể kèm darkVariant — cùng tên token, giá trị khác — và app đổi sang đó khi cần bản tối. Bỏ trống thì giao diện giữ nguyên như đã viết.

Sắp lại tờ lịch

Màn Hôm nay không phải một bố cục cố định — app vẽ nó theo một cây mà giao diện của bạn mô tả được. Bạn đặt thành phần vào những ô có tên, không đặt toạ độ. Toạ độ tự do vỡ trên mọi cỡ màn khác cỡ màn của bạn, và một bố cục hỏng thì không kẹp lại được như một màu hỏng.

Ô chứa minmax Thành phần cho phép
header 02 weekday month-year holiday-banner image divider spacer
hero 11 day-number divider spacer
lunar 02 weekday lunar-date can-chi holiday-banner divider spacer
info 06 lunar-date can-chi holiday-banner solar-term good-hours quote event-list image divider spacer
footer 02 month-year quote image divider spacer

Chạm vào một thành phần thì app quyết định làm gì, theo loại thành phần — giao diện không đặt được. Phông chữ: be-vietnam-pro · playfair-display · bitter

Cấu trúc gói và giới hạn

Tệp .lunayatheme là một zip có theme.json ở gốc, và nếu có ảnh thì một thư mục assets/ bên cạnh. Tên mục phải khớp đúng mẫu dưới đây — đó là danh sách cho phép, chứ không phải danh sách chặn.

ten-giao-dien.lunayatheme
├── theme.json
└── assets/
    └── hoa-van.png
Giới hạnGiá trị
fileBytes2097152
idMaxLength128
nameMaxLength60
authorMaxLength60
descriptionMaxLength200
maxImages2
jsonMaxDepth32
archiveBytes2097152
archiveUncompressedBytes8388608
archiveMaxEntries8
manifestBytes262144
archiveEntryNamePattern^(theme\.json|assets/[a-z0-9._-]{1,64})$
archiveMaxEntryBytes4194304

Khi có gì sai

Lỗi có mã ổn định, nên app và trình soạn trên web có thể khác nhau về câu chữ nhưng không bao giờ khác nhau về nghĩa. ERR_ là từ chối tệp; WARN_ là vẫn nhận và báo cho bạn biết.

Nghĩa
ERR_FORMAT_VERSION_UNSUPPORTEDformatVersion lớn hơn bản app hiểu
ERR_MISSING_REQUIREDThiếu trường bắt buộc
ERR_ID_INVALIDid sai định dạng hoặc quá dài
ERR_COLOR_FORMATMàu không phải #RRGGBB hoặc #AARRGGBB
ERR_TOO_LARGEFile vượt giới hạn dung lượng
ERR_BASEDON_UNKNOWNbasedOn trỏ tới theme dựng sẵn không tồn tại
ERR_SLOT_UNKNOWNTên ô chứa không có trong slots.json
ERR_SLOT_OVERFLOWÔ chứa vượt số thành phần cho phép
ERR_HERO_EMPTYÔ hero bắt buộc có đúng 1 thành phần
ERR_COMPONENT_NOT_ALLOWED_IN_SLOTLoại thành phần không được phép trong ô này
ERR_TOO_MANY_IMAGESVượt số ảnh cho phép
WARN_CONTRAST_LOWTương phản ink/paper dưới 4.5:1
WARN_CONTRAST_VERY_LOWTương phản ink/paper dưới 3.0:1 — rất khó đọc
WARN_COMPONENT_UNKNOWNLoại thành phần không biết — đã bỏ qua
WARN_FONT_SLUG_UNKNOWNSlug font không biết — dùng font mặc định
WARN_VALUE_CLAMPEDGiá trị vượt biên — đã kẹp lại
ERR_ARCHIVE_INVALIDGói nén hỏng hoặc không đọc được
ERR_ARCHIVE_ENTRY_NAMETên mục trong gói không nằm trong danh sách cho phép
ERR_ARCHIVE_TOO_MANY_ENTRIESGói có quá nhiều mục
ERR_ARCHIVE_UNCOMPRESSED_TOO_LARGETổng cỡ sau giải nén vượt trần
ERR_MANIFEST_MISSINGGói thiếu theme.json
ERR_ARCHIVE_ENTRY_TOO_LARGEMột mục giải nén vượt trần (chặn khi đọc, không tin cỡ khai trong zip)
ERR_IMAGE_SRC_UNKNOWNThành phần image trỏ tới khoá không có trong images
ERR_IMAGE_SOURCE_INVALIDNguồn ảnh không phải assets/<tên> hay data: base64
WARN_IMAGE_SRC_MISSINGThành phần image không khai src — sẽ không vẽ gì

Mọi con số trên trang này đọc từ chính đặc tả của app lúc dựng trang, nên nó không thể cũ đi so với app.