diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-05-18 17:02:32 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2026-05-18 17:02:32 +0200 |
| commit | 70994ddaf48a92d062977bc667c72685a4ccb815 (patch) | |
| tree | 262dd69b72804e00356cd8c98bb2a93d5668d368 | |
| parent | 2115198c6a292dce97cfb411e939b10c39ca24c1 (diff) | |
try to use bun for bundling
| -rw-r--r-- | build.zig | 23 | ||||
| -rw-r--r-- | flake.nix | 6 | ||||
| -rw-r--r-- | src/routes/static.zig | 3 | ||||
| -rw-r--r-- | src/storage/media-manager/root.zig | 0 | ||||
| -rw-r--r-- | static/icons/index.js | 41 | ||||
| -rw-r--r-- | static/index.html | 11 | ||||
| -rw-r--r-- | static/index.js | 17 | ||||
| -rw-r--r-- | static/pages/image-viewer/index.css | 29 | ||||
| -rw-r--r-- | static/pages/image-viewer/index.js | 61 | ||||
| -rw-r--r-- | static/pages/login/index.js | 4 | ||||
| -rw-r--r-- | static/pages/main/index.js | 4 | ||||
| -rw-r--r-- | static/pages/settings/index.js | 4 | ||||
| -rw-r--r-- | static/pages/shuffle/index.js | 4 | ||||
| m--------- | static/sfw | 0 | ||||
| -rw-r--r-- | static/tsconfig.json | 10 | ||||
| -rw-r--r-- | static/widgets/editable/index.js | 4 | ||||
| -rw-r--r-- | static/widgets/image/index.css | 1 | ||||
| -rw-r--r-- | static/widgets/image/index.js | 4 | ||||
| -rw-r--r-- | static/widgets/month-select/index.js | 4 | ||||
| -rw-r--r-- | static/widgets/password-dialog/index.js | 4 | ||||
| -rw-r--r-- | static/widgets/search/index.js | 4 | ||||
| -rw-r--r-- | static/widgets/upload-bar/index.js | 4 |
22 files changed, 108 insertions, 134 deletions
@@ -9,6 +9,9 @@ pub fn build(b: *std.Build) void { const disable_auth = b.option(bool, "disable-auth", "disable-authentication") orelse false; opts.addOption(bool, "disable_auth", disable_auth); + const static_path = b.option([]const u8, "static-path", "static-path") orelse "zig-out/dist"; + opts.addOption([]const u8, "static_path", static_path); + const mod = b.addModule("memora", .{ .root_source_file = b.path("src/root.zig"), .target = target, @@ -29,11 +32,29 @@ pub fn build(b: *std.Build) void { }, }), }); - exe.linkLibC(); b.installArtifact(exe); + const fonts_dist = b.addSystemCommand( + &.{ "cp", "-r", "static/fonts", "zig-out/dist" }, + ); + + const dist = b.addSystemCommand( + &.{ + "bun", + "build", + "static/index.html", + "--outdir", + "zig-out/dist", + "--loader", + ".css:text" + } + ); + + b.default_step.dependOn(&dist.step); + dist.step.dependOn(&fonts_dist.step); + const run_step = b.step("run", "Run the app"); const run_cmd = b.addRunArtifact(exe); @@ -7,7 +7,9 @@ outputs = { self, nixpkgs }: let - pkgs = import nixpkgs { system = "x86_64-linux"; }; + pkgs = import nixpkgs { + system = "x86_64-linux"; + }; in { packages.x86_64-linux.default = pkgs.stdenv.mkDerivation { @@ -20,6 +22,7 @@ buildInputs = [ pkgs.zig pkgs.libexif + pkgs.bun ]; }; @@ -27,6 +30,7 @@ packages = [ pkgs.zig pkgs.libexif + pkgs.bun ]; }; }; diff --git a/src/routes/static.zig b/src/routes/static.zig index f52d178..f8f7a6c 100644 --- a/src/routes/static.zig +++ b/src/routes/static.zig @@ -1,5 +1,6 @@ const std = @import("std"); const mime = @import("../mime.zig"); +const config = @import("config"); const memora = @import("memora"); const Context = memora.Context; @@ -9,7 +10,7 @@ const log = std.log.scoped(.fallback); pub const access = .everyone; pub fn get(ctx: *Context) anyerror!memora.Stream { - var static = try std.fs.cwd().openDir("static", .{}); + var static = try std.fs.cwd().openDir(config.static_path, .{}); defer static.close(); if (static.openFile(ctx.request.head.target[1..], .{})) |file| { diff --git a/src/storage/media-manager/root.zig b/src/storage/media-manager/root.zig new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/storage/media-manager/root.zig diff --git a/static/icons/index.js b/static/icons/index.js index 3562754..5402be0 100644 --- a/static/icons/index.js +++ b/static/icons/index.js @@ -1,29 +1,36 @@ import * as sfw from 'sfw'; const { I } = sfw.element.native; -const icons = [ - 'settings', - 'shuffle', - 'calendar', - 'search', - 'close', - 'home', - 'add', - 'edit', - 'check', - 'next', -]; +import Settings from './settings'; +import Shuffle from './shuffle'; +import Calendar from './calendar'; +import Search from './search'; +import Close from './close'; +import Home from './home'; +import Add from './add'; +import Edit from './edit'; +import Check from './check'; +import Next from './next'; -const target = { - icons: await Promise.all( - icons.map(async v => [v, await import(`./${v}.js`)]) - ).then(r => r.reduce((obj, [v, m]) => (obj[v] = m, obj), {})), +const icons = { + 'settings': Settings, + 'shuffle': Shuffle, + 'calendar': Calendar, + 'search': Search, + 'close': Close, + 'home': Home, + 'add': Add, + 'edit': Edit, + 'check': Check, + 'next': Next, }; +const target = { icons: icons }; + const handler = { get(target, prop, receiver) { return I.new({ - innerHTML: target.icons[prop]?.default ?? '?', + innerHTML: target.icons[prop] ?? '?', className: 'icon', }); } diff --git a/static/index.html b/static/index.html index c1aa172..8fa5079 100644 --- a/static/index.html +++ b/static/index.html @@ -2,17 +2,6 @@ <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <script type="importmap"> - { - "imports": { - "sfw": "./sfw/index.js", - "api": "./api/index.js", - "icons": "./icons/index.js", - "widgets": "./widgets/index.js", - "pages": "./pages/index.js" - } - } - </script> <title>Memora</title> <script type="module" src="index.js"></script> <link rel="icon" href="icon.png" type="image/png"> diff --git a/static/index.js b/static/index.js index 5979d86..ddbead7 100644 --- a/static/index.js +++ b/static/index.js @@ -1,7 +1,6 @@ import * as sfw from 'sfw'; import * as api from 'api'; import Month from './month.js'; -import * as service_worker from './service-worker/index.js'; import LoginView from './pages/login/index.js'; import MainView from './pages/main/index.js'; @@ -12,24 +11,18 @@ import SettingsView from './pages/settings/index.js'; import ShuffleView from './pages/shuffle/index.js'; import UploadBar from './widgets/upload-bar/index.js'; -await service_worker.register() - -sfw.theme.add_css(await sfw.css(import.meta.url, './index.css')); +import stylesheet from './index.css'; +sfw.theme.add_css(await sfw.css.sheet(stylesheet)); const image_viewer = ImageViewer.new(); const reload = () => { - image_viewer.clear(); api.images.list().then(images => { const last = Month.from_unix(images[0].timestamp); let first = Month.from_unix(images.findLast(i => i.timestamp).timestamp); month_select.months = first.to(last); - for (const image of images) { - image_viewer.add(image); - } - - image_viewer.preload_all(); + image_viewer.images = images; shuffle.images = images; }); } @@ -90,11 +83,11 @@ const main = MainView.new({ month_select.show(); }, onupload: async () => { - const uploader = await api.images.upload_to_timeline(); - upload_bar.progress = 0; upload_bar.show(); + const uploader = await api.images.upload_to_timeline(); + uploader.onprogress = (count, total) => { upload_bar.progress = count / total; } diff --git a/static/pages/image-viewer/index.css b/static/pages/image-viewer/index.css index 8b57f58..4567050 100644 --- a/static/pages/image-viewer/index.css +++ b/static/pages/image-viewer/index.css @@ -6,33 +6,4 @@ display: grid; gap: 10px; padding: 10px; - padding-bottom: 50%; -} - -#container sfw-image { - margin: auto; -} - -#subtitle { - text-align: center; - color: #7e8fa8; - font-size: 0.8em; - font-weight: lighter; - padding-top: 10px; -} - -#subtitle i { - font-family: 'Pacifico'; - font-style: normal; - color: var(--fg-disabled); -} - -#date { - font-size: 1.2em; - font-weight: bold; - background: #efefef99; - backdrop-filter: blur(10px); - padding: 10px; - border-radius: var(--border-radius); - box-shadow: #223223aa 1px 1px 4px; } diff --git a/static/pages/image-viewer/index.js b/static/pages/image-viewer/index.js index d4c9dd9..34b5f69 100644 --- a/static/pages/image-viewer/index.js +++ b/static/pages/image-viewer/index.js @@ -1,77 +1,34 @@ import * as sfw from 'sfw'; -const { Div, Img } = sfw.element.native; +const { Img, Div } = sfw.element.native; import Month from '../../month.js'; -import Image from '../../widgets/image/index.js'; +import stylesheet from './index.css'; -const css = await sfw.css(import.meta.url, './index.css') +const css = await sfw.css.sheet(stylesheet); export default class ImageViewer extends sfw.element.Container { - #container - #subtitle #images - #current_month + #image_elements constructor() { super({ css }); - this.#current_month = null; - this.#images = []; this.body.append( - this.#container = Div.new({ + Div.new({ id: 'container', - onscroll: () => this.preload_all(), - children: [ - this.#subtitle = Div.new({ - id: 'subtitle', - innerHTML: 'Powered by <i>Memora<i>' - }) - ], + onscroll: () => console.log('scroll'), }) ); } - add(metadata) { - const month = Month.from_unix(metadata.timestamp); - - if (month != this.#current_month && !(month?.is_same(this.#current_month))) { - const content = month == null ? 'No Date' : `${month.name} ${month.year}`; - this.#current_month = month; - const title = Div.new({ - id: 'date', - innerText: content, - }) - this.#container.insertBefore(title, this.#subtitle); - } - - const image = Image.new({ metadata }); - this.#images.push(image); - this.#container.insertBefore(image, this.#subtitle); - } - - clear() { - this.#container.innerHTML = ''; - this.#container.append(this.#subtitle); + set images(images) { + this.#images = images; } jump_to(month) { - const image = this.#images.find(i => i.month == month || i.month.is_same(month)); - image.scrollIntoView({ behavior: 'instant' }) - } - - preload_all() { - this.#images.forEach(image => this.#preload(image)) - } - - #preload(image) { - const loading_zone_after = (this.#container.scrollTop + this.#container.offsetHeight) * 2; - const loading_zone_before = this.#container.scrollTop - image.offsetHeight * 2; - - if (loading_zone_before < image.offsetTop && image.offsetTop < loading_zone_after) { - image.load(); - } + // TODO } } diff --git a/static/pages/login/index.js b/static/pages/login/index.js index 15a6016..0390cc0 100644 --- a/static/pages/login/index.js +++ b/static/pages/login/index.js @@ -2,7 +2,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div, Label, H1: Title, Input, Button, Form } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class LoginView extends sfw.element.Container { #container diff --git a/static/pages/main/index.js b/static/pages/main/index.js index 8c0cac1..b0a13f6 100644 --- a/static/pages/main/index.js +++ b/static/pages/main/index.js @@ -2,7 +2,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div, Input } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class MainView extends sfw.element.Container { #active_kind diff --git a/static/pages/settings/index.js b/static/pages/settings/index.js index c46987b..5cc40be 100644 --- a/static/pages/settings/index.js +++ b/static/pages/settings/index.js @@ -6,7 +6,9 @@ const { Div, Img } = sfw.element.native; import Editable from '../../widgets/editable/index.js'; import PasswordDialog from '../../widgets/password-dialog/index.js'; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class SettingsView extends sfw.element.Container { #profile_image diff --git a/static/pages/shuffle/index.js b/static/pages/shuffle/index.js index 7833de4..620195c 100644 --- a/static/pages/shuffle/index.js +++ b/static/pages/shuffle/index.js @@ -3,7 +3,9 @@ const { Div, Canvas } = sfw.element.native; import { Image } from 'widgets'; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class ShuffleView extends sfw.element.Container { #front_image diff --git a/static/sfw b/static/sfw -Subproject ca647a41364001b6f996715bd2313b44ac28a4d +Subproject 9d74e1679915cc0c5f56413879276fc43b09389 diff --git a/static/tsconfig.json b/static/tsconfig.json new file mode 100644 index 0000000..97d1355 --- /dev/null +++ b/static/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "paths": { + "sfw": ["./sfw"], + "api": ["./api"], + "icons": ["./icons"], + "widgets": ["./widgets"] + }, + } +} diff --git a/static/widgets/editable/index.js b/static/widgets/editable/index.js index 116946f..b7e24de 100644 --- a/static/widgets/editable/index.js +++ b/static/widgets/editable/index.js @@ -2,7 +2,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div, Label, Input } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class Editable extends sfw.element.Container { #label diff --git a/static/widgets/image/index.css b/static/widgets/image/index.css index bfca517..a4105e9 100644 --- a/static/widgets/image/index.css +++ b/static/widgets/image/index.css @@ -6,6 +6,7 @@ overflow: hidden; box-shadow: #223223aa 1px 1px 4px; background: var(--card-background); + margin: auto; } #container img { diff --git a/static/widgets/image/index.js b/static/widgets/image/index.js index 3e403bd..b063864 100644 --- a/static/widgets/image/index.js +++ b/static/widgets/image/index.js @@ -3,7 +3,9 @@ import * as sfw from 'sfw'; import Month from '../../month.js'; const { Div, Img, A: Link } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css') +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class Image extends sfw.element.Container { #container diff --git a/static/widgets/month-select/index.js b/static/widgets/month-select/index.js index 63caf70..7e4d583 100644 --- a/static/widgets/month-select/index.js +++ b/static/widgets/month-select/index.js @@ -2,7 +2,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class MonthSelect extends sfw.element.Container { #container diff --git a/static/widgets/password-dialog/index.js b/static/widgets/password-dialog/index.js index 6dbba12..76fbec6 100644 --- a/static/widgets/password-dialog/index.js +++ b/static/widgets/password-dialog/index.js @@ -3,7 +3,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div, Input } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class PasswordDialog extends sfw.element.Container { #current diff --git a/static/widgets/search/index.js b/static/widgets/search/index.js index 1a367f3..b014cdd 100644 --- a/static/widgets/search/index.js +++ b/static/widgets/search/index.js @@ -2,7 +2,9 @@ import icons from 'icons'; import * as sfw from 'sfw'; const { Div, Input, Button } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css'); +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class Search extends sfw.element.Container { #container diff --git a/static/widgets/upload-bar/index.js b/static/widgets/upload-bar/index.js index a9e0220..f6d37b5 100644 --- a/static/widgets/upload-bar/index.js +++ b/static/widgets/upload-bar/index.js @@ -1,7 +1,9 @@ import * as sfw from 'sfw'; const { Div } = sfw.element.native; -const css = await sfw.css(import.meta.url, './index.css') +import stylesheet from './index.css'; + +const css = await sfw.css.sheet(stylesheet); export default class UploadBar extends sfw.element.Container { #files_progress |