Rename project from TopDeck to Canon
- Module github.com/cdnutter/canon, binary/command cmd/canon, Docker image and compose project "canon" - Environment variables now use the CANON_ prefix - Default site title is "<Your name>'s Canon" (or "Canon" with no name); a Site title set in Settings still overrides it - Existing topdeck.db (and WAL files) is renamed to canon.db on start - README: upgrade notes, including moving the old compose volume Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
58a29b7339
commit
3923444b3b
26 files changed
+215
-120
No files matched your search
@@ -1,5 +1,5 @@
|
||||
/* ==========================================================================
|
||||
TopDeck. Colors and fonts come from themes.css.
|
||||
Canon. Colors and fonts come from themes.css.
|
||||
========================================================================== */
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* TopDeck theme presets.
|
||||
* Canon theme presets.
|
||||
*
|
||||
* Every color on the site comes from these variables, so a custom theme only
|
||||
* needs to override them (Settings → Appearance → Custom CSS), e.g.
|
||||
|
||||
@@ -70,7 +70,10 @@ function profileSection() {
|
||||
});
|
||||
|
||||
const ownerName = h("input", { maxlength: 100, value: settings.ownerName, placeholder: "Your name" });
|
||||
const siteTitle = h("input", { maxlength: 100, value: settings.siteTitle, placeholder: "TopDeck" });
|
||||
// Mirrors DefaultTitle in render.go.
|
||||
const defaultTitle = (name) => (name.trim() ? `${name.trim()}'s Canon` : "Canon");
|
||||
const siteTitle = h("input", { maxlength: 100, value: settings.siteTitle, placeholder: defaultTitle(settings.ownerName) });
|
||||
ownerName.addEventListener("input", () => { siteTitle.placeholder = defaultTitle(ownerName.value); });
|
||||
const tagline = h("input", { maxlength: 300, value: settings.tagline, placeholder: "A line about you or your taste" });
|
||||
const preview = h("input", { type: "number", min: 0, max: 50, value: settings.previewCount, style: { maxWidth: "120px" } });
|
||||
const notes = h("input", { type: "checkbox", checked: settings.showNotes });
|
||||
@@ -78,7 +81,7 @@ function profileSection() {
|
||||
|
||||
const form = h("form", { class: "stack" },
|
||||
h("div", { class: "avatar-edit" }, avatarBox, h("div", { class: "row-btns", style: { display: "flex", gap: "6px", flexWrap: "wrap" } }, uploadBtn, removeBtn, file)),
|
||||
h("div", { class: "row" }, field("Your name", ownerName, "Shown as the page heading."), field("Site title", siteTitle, "Shown in the header and browser tab.")),
|
||||
h("div", { class: "row" }, field("Your name", ownerName, "Shown as the page heading."), field("Site title", siteTitle, "Shown in the header and browser tab. Leave blank to use “Your name's Canon”.")),
|
||||
field("Tagline", tagline),
|
||||
field("Entries per list on the home page", preview, "Visitors see the full list on each list's page. 0 shows only the heading."),
|
||||
h("label", { class: "check" }, notes, "Show my notes to visitors"),
|
||||
@@ -93,7 +96,7 @@ function profileSection() {
|
||||
if (ok) {
|
||||
renderAvatar();
|
||||
const brand = document.querySelector(".brand-name");
|
||||
if (brand) brand.textContent = settings.siteTitle || "TopDeck";
|
||||
if (brand) brand.textContent = settings.siteTitleShown;
|
||||
}
|
||||
});
|
||||
renderAvatar();
|
||||
@@ -181,7 +184,7 @@ function secretInput(key, label, hintHTML) {
|
||||
disabled: st.locked,
|
||||
});
|
||||
const hint = h("span", { class: "field-hint" });
|
||||
if (st.locked) hint.textContent = `Set by the ${"TOPDECK_" + key.toUpperCase()} environment variable.`;
|
||||
if (st.locked) hint.textContent = `Set by the ${"CANON_" + key.toUpperCase()} environment variable.`;
|
||||
else if (hintHTML) hint.append(...hintHTML);
|
||||
const wrap = field(label, input);
|
||||
wrap.append(hint);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Shared helpers for TopDeck's pages. No build step, no dependencies.
|
||||
// Shared helpers for Canon's pages. No build step, no dependencies.
|
||||
|
||||
/** Create an element: h("button", { class: "btn", onclick }, "Save") */
|
||||
export function h(tag, attrs = {}, ...children) {
|
||||
@@ -53,7 +53,7 @@ export class APIError extends Error {
|
||||
|
||||
/** Call the admin JSON API. Throws APIError with a human message. */
|
||||
export async function api(method, path, body) {
|
||||
const opts = { method, headers: { "X-TopDeck": "1" }, credentials: "same-origin" };
|
||||
const opts = { method, headers: { "X-Canon": "1" }, credentials: "same-origin" };
|
||||
if (body instanceof FormData) {
|
||||
opts.body = body;
|
||||
} else if (body !== undefined) {
|
||||
|
||||
@@ -75,7 +75,7 @@ document.addEventListener("click", (e) => {
|
||||
// Grid / list toggle on list pages, remembered per list in this browser.
|
||||
const toggle = document.querySelector("[data-layout-toggle]");
|
||||
if (toggle) {
|
||||
const key = "topdeck:layout:" + toggle.dataset.key;
|
||||
const key = "canon:layout:" + toggle.dataset.key;
|
||||
const apply = (layout) => {
|
||||
for (const { ol } of lists) {
|
||||
ol.classList.toggle("items--grid", layout === "grid");
|
||||
|
||||
Reference in new issue
Block a user