diff --git a/.gitignore b/.gitignore index 051d5bd..f1718a2 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,8 @@ # production /build +/dist +/*.xdc # misc .DS_Store diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ddb1997 --- /dev/null +++ b/Makefile @@ -0,0 +1,47 @@ +.PHONY: help install dev build build-xdc package clean + +.DEFAULT_GOAL := help + +PNPM ?= pnpm +APP_NAME := squig +OUT_DIR := out +DIST_DIR := dist +OUT_XDC := $(DIST_DIR)/$(APP_NAME).xdc + +help: + @echo "squig" + @echo "" + @echo " make install pnpm install" + @echo " make dev Next.js dev server" + @echo " make build Production Next.js build (server / squig.sh)" + @echo " make build-xdc Static export + Webxdc archive → $(OUT_XDC)" + @echo " make package Alias for make build-xdc" + @echo " make clean Remove .next/, out/, dist/" + +install: + $(PNPM) install + +dev: + $(PNPM) dev + +build: + $(PNPM) build + +# Fully offline Delta Chat mini-app: static HTML/JS/CSS + manifest at zip root. +# webxdc.js is a local-dev shim only — the messenger injects its own at runtime. +build-xdc: + @command -v zip >/dev/null || { echo "zip is required to package .xdc"; exit 1; } + WEBXDC=1 $(PNPM) build + @test -f $(OUT_DIR)/index.html || { echo "static export missing $(OUT_DIR)/index.html"; exit 1; } + @test -f $(OUT_DIR)/manifest.toml || { echo "missing $(OUT_DIR)/manifest.toml (add public/manifest.toml)"; exit 1; } + rm -rf $(DIST_DIR) + mkdir -p $(DIST_DIR) + cd $(OUT_DIR) && zip -9 -r ../$(OUT_XDC) . -x webxdc.js -x 'webxdc.js' + @echo "" + @echo " → $(OUT_XDC)" + @echo " Send this file into a Delta Chat chat and tap Start." + +package: build-xdc + +clean: + rm -rf .next out dist diff --git a/app/opengraph-image.tsx b/app/opengraph-image.tsx index 3f7a4c3..b51a276 100644 --- a/app/opengraph-image.tsx +++ b/app/opengraph-image.tsx @@ -3,6 +3,8 @@ import { createSocialImage } from "./social-image"; export const alt = "A blue doodle of a bear sketching a wireframe"; export const size = { width: 1200, height: 630 }; export const contentType = "image/png"; +// Required for `output: "export"` (webxdc packaging). +export const dynamic = "force-static"; export default function OpenGraphImage() { return createSocialImage(); diff --git a/app/twitter-image.tsx b/app/twitter-image.tsx index d31491c..b2acf42 100644 --- a/app/twitter-image.tsx +++ b/app/twitter-image.tsx @@ -3,6 +3,8 @@ import { createSocialImage } from "./social-image"; export const alt = "A blue doodle of a bear sketching a wireframe"; export const size = { width: 1200, height: 630 }; export const contentType = "image/png"; +// Required for `output: "export"` (webxdc packaging). +export const dynamic = "force-static"; export default function TwitterImage() { return createSocialImage(); diff --git a/next.config.ts b/next.config.ts index b39ddf4..3a7d93e 100644 --- a/next.config.ts +++ b/next.config.ts @@ -2,6 +2,10 @@ import type { NextConfig } from "next"; import path from "node:path"; import { fileURLToPath } from "node:url"; +// Webxdc packaging (`make build-xdc`) needs a fully static site at out/. +// Normal `pnpm build` keeps the default Next server output for squig.sh. +const webxdc = process.env.WEBXDC === "1"; + const nextConfig: NextConfig = { // Pin the workspace root to this repo. // @@ -14,6 +18,13 @@ const nextConfig: NextConfig = { turbopack: { root: path.dirname(fileURLToPath(import.meta.url)), }, + ...(webxdc + ? { + output: "export" as const, + // next/image optimizers need a server; webxdc ships plain files. + images: { unoptimized: true }, + } + : {}), }; export default nextConfig; diff --git a/package.json b/package.json index f880d90..4ec5920 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "next dev", "build": "next build", + "build:xdc": "make build-xdc", "start": "next start", "lint": "eslint", "test": "tsc --noEmit -p scripts/tsconfig.json && node --experimental-strip-types --import ./scripts/register-loader.mjs scripts/test-geometry.ts && node --experimental-strip-types --import ./scripts/register-loader.mjs scripts/test-selection.ts && node --experimental-strip-types --import ./scripts/register-loader.mjs scripts/test-clipboard.ts && node --experimental-strip-types --import ./scripts/register-loader.mjs scripts/test-text.ts" diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000..592b11f Binary files /dev/null and b/public/icon.png differ diff --git a/public/manifest.toml b/public/manifest.toml new file mode 100644 index 0000000..93bcceb --- /dev/null +++ b/public/manifest.toml @@ -0,0 +1,4 @@ +name = "squig" +description = "Wireframes that know they're wireframes — an infinite sketch canvas of real UI components." +source_code_url = "https://github.com/pablostanley/squig" +icon = "icon.png" diff --git a/public/webxdc.js b/public/webxdc.js new file mode 100644 index 0000000..ea1d591 --- /dev/null +++ b/public/webxdc.js @@ -0,0 +1,21 @@ +// Local-dev shim only. Delta Chat injects window.webxdc at runtime — +// never ship this file inside the .xdc (make build-xdc excludes it). +if (typeof window !== "undefined" && !window.webxdc) { + window.webxdc = { + sendUpdate() {}, + setUpdateListener(cb) { + return Promise.resolve().then(() => cb && undefined); + }, + getAllUpdates() { + return Promise.resolve([]); + }, + sendToChat() { + return Promise.resolve(); + }, + importFiles() { + return Promise.resolve([]); + }, + selfAddr: "dev@local", + selfName: "Dev", + }; +}