Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@
# `wrangler types --check`, and therefore the build, fail spuriously.
* text=auto eol=lf
*.png binary
*.svg text eol=lf
*.svg text eol=lf
# Wrangler regenerates this in full; it is ~14,800 lines of pinned workerd
# runtime typings around ~20 lines of this project's own bindings. Marking it
# generated keeps it out of diffs and out of the language breakdown.
worker-configuration.d.ts linguist-generated=true
38 changes: 5 additions & 33 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,23 @@
"test": "npm run test:unit -- --run && npm run test:e2e",
"test:e2e": "playwright install && playwright test"
},
"dependencies": {
"@libsql/client": "^0.17.3",
"arctic": "^3.7.0",
"drizzle-orm": "^0.45.2"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@fontsource-variable/outfit": "^5.3.0",
"@fontsource-variable/work-sans": "^5.3.0",
"@libsql/client": "^0.17.3",
"@playwright/test": "^1.60.0",
"@sveltejs/adapter-cloudflare": "^7.2.8",
"@sveltejs/kit": "^2.63.0",
"@sveltejs/vite-plugin-svelte": "^7.1.2",
"@tailwindcss/vite": "^4.3.0",
"@types/node": "^24",
"@vitest/browser-playwright": "^4.1.8",
"arctic": "^3.7.0",
"drizzle-kit": "^0.31.10",
"drizzle-orm": "^0.45.2",
"eslint": "^10.4.1",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-svelte": "^3.19.0",
Expand Down
23 changes: 5 additions & 18 deletions src/lib/components/media/CalendarFeed.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { enhance } from '$app/forms';
import Icon from '$lib/components/ui/Icon.svelte';
import { toasts } from '$lib/stores/toasts.svelte';
import { absorb } from '$lib/forms/feedback';
import { absorb, copyToClipboard } from '$lib/forms/feedback';
import { subscribeLinks } from '$lib/domain/calendar';
import type { SubmitFunction } from '@sveltejs/kit';

Expand Down Expand Up @@ -37,23 +37,10 @@
};
};

/**
* Copy, or fall back to selecting the text.
*
* `navigator.clipboard` needs a secure context and a permission that can be
* refused. When it is not there, leaving the URL selected turns the failure
* into one keystroke rather than a dead button.
*/
async function copy() {
if (!url) return;
try {
await navigator.clipboard.writeText(url);
toasts.add('Calendar link copied');
} catch {
field?.select();
toasts.add('Press Ctrl/Cmd + C to copy the link', 'info');
}
}
/** The field is already on screen here, so refusal only has to select it. */
const copy = () =>
url &&
copyToClipboard(url, { success: 'Calendar link copied', onRefused: () => field?.select() });
</script>

<section
Expand Down
25 changes: 12 additions & 13 deletions src/lib/components/media/ShareButton.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import { tick } from 'svelte';
import Icon from '$lib/components/ui/Icon.svelte';
import { toasts } from '$lib/stores/toasts.svelte';
import { copyToClipboard } from '$lib/forms/feedback';

/**
* Hand this title's public link to somebody.
Expand Down Expand Up @@ -45,18 +45,17 @@
}
}

try {
await navigator.clipboard.writeText(url);
toasts.add('Link copied');
return;
} catch {
revealed = true;
}

// After the input has been rendered, so there is something to select.
await tick();
field?.select();
toasts.add('Press Ctrl/Cmd + C to copy the link', 'info');
// Unlike the panels, this button has no field on screen until the clipboard
// has actually refused — so revealing one is part of the fallback, and the
// tick is what gives it time to exist before it is selected.
await copyToClipboard(url, {
success: 'Link copied',
onRefused: async () => {
revealed = true;
await tick();
field?.select();
}
});
}
</script>

Expand Down
Loading