aboutsummaryrefslogtreecommitdiffstats
path: root/internal/web/static
Commit message (Collapse)AuthorAgeFilesLines
* Add a search box module and a search pagegrm2026-09-161-0/+12
| | | | | | | | | | | | | | | | | | Readers had no way to find a post. The new "search" module goes in the header (a bar under the nav) or a side column (a box with a heading) and is a plain GET form to /search, so it works without JavaScript. The results page lists the published posts of every page whose title or Markdown body matches the query — case-insensitive, each word literal, spaces meaning "anything in between", in order — as title, date and a short snippet with the match marked, 20 per page. modules.kind is a CHECK constraint, so a migration widens it; "search" becomes a reserved page slug so the literal route keeps winning over /{page}. moduleHasSettings now takes the module: a header search box has no heading, hence nothing to edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Make the post form's tag input a box with a folded list of chipsgrm2026-09-151-4/+7
| | | | | | | | | | | A checkbox per existing tag stops working once a blog has dozens of them. The form is now one comma-separated box, prefilled with the post's tags, and the blog's tags sit folded under it as chips; a small script toggles a clicked chip in the box, and without it the chips are a plain list to copy from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Add post tags, with a tag page and two side-column modulesgrm2026-09-152-0/+13
| | | | | | | | | | | | | | | | | | | | Posts can now carry tags, set on the post form as a checklist of the blog's existing tags plus a comma-separated box for new ones (no JS). A tag is a name and a unique slug, so "Go" and "go" are one tag and Greek tags get readable URLs; tags no post uses any more are deleted. On the blog, tags appear under the post date and link to /tag/<slug>, which lists the published posts from every page, paginated like a page. Two new layout modules show them: a Tags list (with counts, by use) and a Tag cloud (alphabetical, sized by use). Both are only fetched when a visible module needs them. The article loop and pager move to a shared postlist partial; while there, the pager stops adding a trailing slash — /news/?p=2 was a 404 because a {page} wildcard never matches one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Give the editor a Markdown toolbar and preview, restyle the dashboardgrm2026-09-151-151/+191
| | | | | | | | | | | | | | | | | | | | The post/page/announcement editor was a bare textarea, so bloggers had to know the Markdown syntax by heart. It now has a toolbar (bold, italic, strike, heading cycle, quote, code, lists, rule, link box, insert file), Ctrl+B/I/K, list continuation on Enter and a Write/Preview toggle that renders the text through the same goldmark + bluemonday pipeline a save uses (POST /b/{sub}/preview; nothing is stored). Every edit goes through execCommand("insertText") so browser undo keeps working. The toolbar stays hidden without JavaScript, leaving the old form untouched. The dashboard moves from the "paper & ink" look to a lean one: neutral surfaces, 1px borders, one blue accent, system sans, with custom properties and flexbox/grid now allowed there (blog.css is unchanged). Class names were kept so the templates barely change; a global [hidden] rule keeps flex containers from overriding the hidden attribute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Turn the image library into a file library, with a per-blog upload limitgrm2026-09-141-7/+18
| | | | | | | | | | | | | | | | | | | | | | | | | | Bloggers want to attach PDFs, archives, audio and other files to posts, not only images. The Images tab becomes Files: any type is accepted, listed by kind with search, paging, rename and multi-file upload, and the editor's paste/drop/"Insert file" takes anything (images are shown, everything else becomes a link). The default limit goes from 5 to 10 MB and the superadmin can override it per blog from /admin/. Files stay in Postgres so one pg_dump is still the whole blog. The bytea column is STORAGE EXTERNAL and /media streams it in substring() slices, so serving never holds a whole file in memory whatever limit a blog gets. Serving any type on the root domain, which carries the session cookie, needs a policy: uploads are typed by sniffing (the extension may only refine a generic sniff to an allowlisted type) and only images, PDF, plain text, audio and video render inline; HTML, SVG, XML, scripts, archives and binaries always go out as application/octet-stream with Content-Disposition: attachment. The body cap moves out of requireAuth into guardPOST, which runs after withBlog has resolved the blog and so knows its limit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Let the blogger set a post's date, when writing it and afterwardsgrm2026-09-141-1/+1
| | | | | | | | | | | The post date is posts.created_at: it already drives ordering, the archive, the feed and the displayed dates, so making it editable is a form field rather than a new column. The datetime-local input degrades to a text box on old browsers, so the server also accepts the value typed by hand; blank keeps the current date. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Drop the content width option: the page is always full widthgrm2026-09-131-2/+1
| | | | | | | | | | With the column grid, a centred max-width box meant the main content did not fill the page when no side columns were shown. The column percentages now divide the whole page; a blogger who wants a narrower, centred main column keeps the columns' space instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Add the Layout tab: five areas with modules, columns and a menu editorgrm2026-09-132-27/+68
| | | | | | | | | | | | | | | | | | | A blog page is now header, left column, main content, right column and footer, each holding an ordered list of modules (blog title, logo, menu, archive by year/month, recent posts, custom HTML, footer text, RSS link, site map). Side columns have percentage widths, can be hidden, and can keep their space when empty; on phones they stack under the posts. The menu becomes its own ordered list mixing pages and outside links. Announcements are placed per column at its top or bottom. Modules and menu items get tables; the migration derives them from each blog's old theme (nav position, footer text, show-in-nav pages) so existing blogs look the same. Custom HTML is deliberately stored and served as-is (owner's decision; see AGENTS.md for why the blast radius is the blogger's own origin). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Add a favicon and rework the Design pagegrm2026-09-124-3/+50
| | | | | | | | | | | | | | | | | | | | | Blogs had no icon at all, and browsers probing /favicon.ico ended up rendering the 404 page. Ship a default paper & ink icon (svg + png) and let each blog replace it from the Design page; /favicon.ico now serves the default or redirects to the blog's own image. ICO uploads accepted. The Design page gains colour schemes, reset to defaults, thumbnail image pickers, jump links and a sticky save bar, plus new options: heading font, link underline/hover, content box style and padding, header image height, post dates and format, footer RSS toggle, menu alignment/style/hover and sidebar width. The Menu card picks the position with illustrated tiles and shows only the options that apply to it, using CSS :checked rules rather than JavaScript. All new defaults reproduce the previous look, so existing blogs are unchanged until edited. Theme is jsonb, so no migration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Add announcements: blog-wide notices on every page and postgrm2026-09-121-0/+11
| | | | | | | | | | | A blog can have any number of announcements ("sections" in the schema): a book club's next meeting, this month's pick, a holiday closure. Each has a placement (above or below the posts, or under the menu when the menu is a left sidebar), a look (highlighted, warning, plain), an order, and an on/off switch so recurring notices can be kept around while hidden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Insert images straight from the Markdown editorgrm2026-09-121-1/+7
| | | | | | | | | | | A shared editor partial adds an "Insert image" file picker plus paste and drag-and-drop on the textarea; a few lines of inline JS upload the file to the existing images endpoint (which now answers JSON when asked) and put the Markdown at the caret. Without JS the same file rides along with the form and is appended to the text on save, so nothing depends on scripting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Redesign the dashboard: paper & inkgrm2026-09-121-62/+115
| | | | | | | | | | | | Flat colours, 2px rules, hard offset shadows and square corners; mono headings and labels; the blog nav is a tab strip that scrolls sideways on phones instead of stacking; list pages get a page header with the primary action on the right; the Markdown cheat-sheet collapses into a details element so forms stay calm. Plain CSS only, no variables or flex, so it still renders on old browsers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Highlight the current tab in the dashboard navgrm2026-09-121-0/+2
| | | | | Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A
* Initial multi-tenant blog hostgramanas2026-09-122-0/+137
Go + Postgres application serving a management dashboard on the base domain and one public blog per subdomain. Markdown posts organised in pages, form-based theme customisation, image uploads stored in Postgres, JWT cookie sessions with CSRF, superadmin user management, RSS feeds. Docker/compose deployment and a Makefile-driven dev environment with seed data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A