canvas-free.openapps.xyz

Source

This app’s code and revision history are public. Download a copy or explore the files below.

Public
The app’s code and revision history are public.
Owned app · rev 93fdaafeda16
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 788ba0fa01083b300ad50913
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
← All files
src/lib/mine.ts
// "Your boards" and your name: what this browser remembers, kept on this device only. The keys a board's
// links carry stay here too, so a returning owner or editor opens their boards without the link.
export interface MyBoard { id: string; title: string; edit?: string; admin?: string; at: number }

const KEY = "canvas:boards";

export function myBoards(): MyBoard[] {
  try {
    const list = JSON.parse(localStorage.getItem(KEY) || "[]");
    return Array.isArray(list) ? list.filter((b) => b && typeof b.id === "string") : [];
  } catch {
    return [];
  }
}

export function rememberBoard(board: Partial<MyBoard> & { id: string }) {
  const list = myBoards();
  const prev = list.find((b) => b.id === board.id);
  const next = { title: "", ...prev, ...board, at: Date.now() };
  try {
    localStorage.setItem(KEY, JSON.stringify([next, ...list.filter((b) => b.id !== board.id)].slice(0, 200)));
  } catch { /* storage full or blocked: the links still work */ }
}

export function forgetBoard(id: string) {
  try { localStorage.setItem(KEY, JSON.stringify(myBoards().filter((b) => b.id !== id))); } catch { /* ignore */ }
}

export const boardOf = (id: string) => myBoards().find((b) => b.id === id);

export const editPath = (id: string, key: string) => `/b/${id}#edit=${key}`;

const NAME_KEY = "canvas:name";
const ADJ = ["Blue", "Green", "Golden", "Quiet", "Swift", "Bright", "Calm", "Clever", "Brave", "Kind", "Lucky", "Sunny"];
const ANIMALS = ["Otter", "Fox", "Heron", "Panda", "Lynx", "Robin", "Koala", "Badger", "Falcon", "Seal", "Wren", "Moose"];

/** The name shown next to this person's pointer. A friendly random one until they pick their own. */
export function myName(): string {
  try {
    const saved = localStorage.getItem(NAME_KEY);
    if (saved) return saved;
    const made = `${ADJ[Math.floor(Math.random() * ADJ.length)]} ${ANIMALS[Math.floor(Math.random() * ANIMALS.length)]}`;
    localStorage.setItem(NAME_KEY, made);
    return made;
  } catch {
    return "Guest";
  }
}

export function setMyName(name: string) {
  try { localStorage.setItem(NAME_KEY, name.trim().slice(0, 60) || "Guest"); } catch { /* ignore */ }
}

Change history

Point OpenApps links to openapps.free
93fdaafeda · 18 hours ago
Bind AGENT_ADDRESS in policy constants
bf8f43b096 · 20 hours ago
Use functions.openapps.xyz alias, add OpenApp contribution strip
56f1a381b2 · 20 hours ago
Bounded source sync
9ec707aeb8 · 1 day ago
Bounded source sync
5645481c23 · 1 day ago
Bounded source sync
c38fe71874 · 1 day ago
Bounded source sync
28c0350b40 · 1 day ago