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
80 changes: 80 additions & 0 deletions .claude/commands/release-scrolly.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
Take all uncommitted and committed changes on the current branch and drive them through the full release pipeline until a new Docker image is published. Follow every step below in order.

## 1. Commit & Push Changes

- Run `git status` and `git diff` to see what's uncommitted
- Break uncommitted changes into small, focused conventional commits (feat:, fix:, refactor:, chore:, etc.)
- If pre-commit hooks fail, fix the issue and create a NEW commit (never amend)
- If already on main, create a feature branch first (`git checkout -b <descriptive-branch-name>`)
- Push the branch to origin

## 2. Create a PR

- Use `gh pr create` targeting main
- Title should summarize the changes concisely (under 70 chars)
- Body should have a ## Summary with bullet points and a ## Test plan
- If a PR already exists for this branch, skip creation

## 3. Pass CI Checks

- Watch PR checks with `gh pr checks <number> --watch`
- If any check fails, read the failure logs, fix the issue, commit the fix, and push
- Repeat until ALL checks pass (lint, format, type-check, tests, build, CodeQL)
- Run `npm run check` and `npm run lint` locally before pushing fixes to save time

## 4. Merge the PR

- Once all checks are green, merge with `gh pr merge <number> --squash --delete-branch`
- If merge is blocked, investigate why (reviews required, checks pending) and address it
- Confirm merge succeeded

## 5. Wait for Release-Please PR

- After merge to main, the Release workflow runs and release-please creates/updates a release PR
- Poll with `gh pr list --label "autorelease: pending"` until the release PR appears
- Note the release PR number

## 6. Wait for Release PR Checks

- The release PR gets CI checks via release-pr-checks.yml (triggered by close/reopen)
- Watch with `gh pr checks <release-pr-number> --watch`
- If checks fail, you may need to push fixes to main and wait for release-please to update the PR

## 7. Merge the Release PR

- Once checks pass: `gh pr merge <release-pr-number> --squash --delete-branch`
- This triggers release-please to publish a GitHub release with version tag

## 8. Verify the Release

- Confirm the GitHub release was created: `gh release list --limit 1`
- Note the version number

## 9. Wait for Docker Image

- The docker-publish.yml workflow triggers automatically after the Release workflow completes
- Watch it: `gh run list --workflow=docker-publish.yml --limit 1` and `gh run watch <run-id>`
- If it doesn't trigger within 2 minutes, check if the workflow_run event fired
- As a fallback, manually trigger: `gh workflow run docker-publish.yml -f version=<version>`

## 10. Confirm Docker Image Published

- Verify the image exists: `gh api /orgs/312-dev/packages/container/scrolly/versions --jq '.[0].metadata.container.tags'`
- Confirm both `<version>` and `latest` tags are present
- Report the full image reference: `ghcr.io/312-dev/scrolly:<version>`

## 11. Update Documentation

- Review the changes that were released and check if any docs in `docs/` are now outdated or would benefit from updates
- Key docs to check: `docs/api.md` (new/changed endpoints), `docs/data-model.md` (schema changes), `docs/architecture.md` (structural changes, directory tree, ASCII diagrams), `docs/design-guidelines.md` (UI/component changes), `docs/notifications.md` (notification changes)
- Check any ASCII diagrams, directory trees, or architecture diagrams in the docs for inaccuracies — if files were added/removed/moved or the architecture changed, update the diagrams to match reality
- Only update docs that are **clearly affected** by the changes — don't touch docs that are still accurate
- If docs were updated, commit them as `docs: update <file> for <change>` and push directly to main
- Skip this step entirely if the changes are purely internal (refactors, dep bumps, CI tweaks) with no user-facing or API impact

## Important Notes

- Never force-push to main
- If anything gets stuck, check `gh run list` for workflow status and `gh run view <id> --log-failed` for errors
- The full pipeline (CI + release + Docker build) takes ~15-20 minutes total
- Keep the user informed of progress at each major step
22 changes: 22 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
},
"dependencies": {
"better-sqlite3": "^12.6.2",
"bplist-parser": "^0.3.2",
"drizzle-orm": "^0.45.1",
"mrmime": "^2.0.1",
"pino": "^10.3.1",
Expand Down
4 changes: 2 additions & 2 deletions src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover, interactive-widget=resizes-content" />
<meta name="description" content="Private video sharing for your friend group" />
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)" />
<meta name="theme-color" content="#FFFFFF" media="(prefers-color-scheme: light)" />
Expand All @@ -15,7 +15,7 @@
<meta property="og:type" content="website" />
<meta property="og:image" content="/icons/icon-512.png" />
<link rel="manifest" href="/manifest.json" />
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png" />
<link rel="apple-touch-icon" href="/icon/apple-touch-icon.svg" />
<link rel="icon" id="dynamic-favicon" type="image/svg+xml" href="/icon/favicon.svg" />
<link rel="icon" href="/favicon-32.png" sizes="32x32" type="image/png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
Expand Down
8 changes: 7 additions & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Handle, RequestEvent } from '@sveltejs/kit';
import { getUserIdFromCookies, getUserWithGroup } from '$lib/server/auth';
import { getUserIdFromCookies, getUserWithGroup, getDefaultGroup } from '$lib/server/auth';
import { getAccentColor } from '$lib/colors';
import { startScheduler } from '$lib/server/scheduler';
import { createLogger } from '$lib/server/logger';
Expand Down Expand Up @@ -103,6 +103,12 @@ export const handle: Handle = async ({ event, resolve }) => {
}
}

// For unauthenticated requests, still resolve the group so accent color,
// dynamic icons, and PWA branding work on /join, /onboard, etc.
if (!event.locals.group) {
event.locals.group = await getDefaultGroup();
}

const response = await resolve(event, {
transformPageChunk: ({ html }) => {
const theme = event.locals.user?.themePreference;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/AddVideo.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@
<input
bind:this={urlInput}
type="url"
inputmode="url"
autocomplete="off"
bind:value={url}
placeholder="Paste a link..."
disabled={loading}
Expand Down Expand Up @@ -468,7 +470,6 @@
color: var(--error);
text-align: center;
}

.no-provider-state {
align-items: center;
justify-content: center;
Expand All @@ -491,7 +492,6 @@
color: var(--text-primary);
margin: 0 0 var(--space-xs);
}

.no-provider-desc {
font-size: 0.875rem;
color: var(--text-muted);
Expand Down
7 changes: 6 additions & 1 deletion src/lib/components/BaseSheet.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { Snippet } from 'svelte';
import { pushState } from '$app/navigation';
import { pushState, beforeNavigate } from '$app/navigation';
import { onDestroy } from 'svelte';

let {
Expand All @@ -23,6 +23,11 @@
let closedViaBack = false;
let timers: ReturnType<typeof setTimeout>[] = [];

// Prevent history.back() in cleanup when a real navigation occurs (e.g. clicking a link inside the sheet)
beforeNavigate(() => {
closedViaBack = true;
});

function safeTimeout(fn: () => void, ms: number) {
const id = setTimeout(fn, ms);
timers.push(id);
Expand Down
44 changes: 41 additions & 3 deletions src/lib/components/FilterBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
onfilter,
swipeProgress = 0,
swiping = false,
hidden = false
hidden = false,
unwatchedCount = 0,
pullOffset = 0
}: {
filter: FeedFilter;
onfilter: (f: FeedFilter) => void;
swipeProgress?: number;
swiping?: boolean;
hidden?: boolean;
unwatchedCount?: number;
pullOffset?: number;
} = $props();

const filters: FeedFilter[] = ['unwatched', 'watched', 'favorites'];
Expand Down Expand Up @@ -57,7 +61,12 @@
});
</script>

<div class="filter-bar" class:ui-hidden={hidden}>
<div
class="filter-bar"
class:ui-hidden={hidden}
class:pull-snapping={pullOffset === 0}
style:transform={pullOffset > 0 ? `translateY(${pullOffset}px)` : undefined}
>
<div class="filter-tabs" role="tablist" bind:this={containerEl}>
{#each filters as f, i (f)}
<button
Expand All @@ -66,7 +75,12 @@
class:active={filter === f}
onclick={() => onfilter(f)}
>
<span class="tab-label" bind:this={labelEls[i]}>{labels[i]}</span>
<span class="tab-label" bind:this={labelEls[i]}>
{labels[i]}
{#if f === 'unwatched' && unwatchedCount > 0}
<span class="badge">{unwatchedCount > 99 ? '99+' : unwatchedCount}</span>
{/if}
</span>
</button>
{/each}
<div class="tab-indicator" class:instant={swiping} style={indicatorStyle}></div>
Expand Down Expand Up @@ -97,6 +111,12 @@
opacity: 0;
}

.filter-bar.pull-snapping {
transition:
opacity 0.3s ease,
transform 0.25s ease;
}

.filter-tabs {
position: relative;
display: flex;
Expand All @@ -122,6 +142,24 @@
color: var(--reel-text);
}

.badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 5px;
margin-left: 4px;
background: var(--accent-magenta);
color: #fff;
font-family: var(--font-body);
font-size: 0.6875rem;
font-weight: 700;
line-height: 1;
border-radius: var(--radius-full);
vertical-align: middle;
}

.tab-indicator {
position: absolute;
bottom: 0;
Expand Down
4 changes: 2 additions & 2 deletions src/lib/components/InstallBanner.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<button class="smart-close" onclick={dismissInstall} aria-label="Close">
<XIcon size={16} />
</button>
<img class="smart-icon" src="/icons/apple-touch-icon.png" alt="scrolly" />
<img class="smart-icon" src="/icon/apple-touch-icon.svg" alt="scrolly" />
<div class="smart-text">
<strong>scrolly</strong>
<span>Add to your Home Screen</span>
Expand Down Expand Up @@ -68,7 +68,7 @@
<button class="banner-close" onclick={dismissInstall} aria-label="Close">
<XIcon size={14} />
</button>
<img class="banner-icon" src="/icons/icon-192.png" alt="" />
<img class="banner-icon" src="/icon/icon-192.svg" alt="" />
<div class="banner-text">
<strong>scrolly</strong>
<span>Your crew's private feed</span>
Expand Down
2 changes: 2 additions & 0 deletions src/lib/components/MentionInput.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
bind:this={inputEl}
bind:value={text}
type="text"
autocomplete="off"
class="overlay-input"
{placeholder}
{maxlength}
Expand All @@ -205,6 +206,7 @@
<textarea
bind:this={inputEl}
bind:value={text}
autocomplete="off"
class="overlay-input"
{placeholder}
{maxlength}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/ProgressBar.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
left: 0;
right: 0;
z-index: 6;
height: 24px;
height: 48px;
display: flex;
align-items: center;
cursor: pointer;
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/ReelOverlayActions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
<textarea
bind:this={inputEl}
bind:value={editValue}
autocomplete="off"
onkeydown={handleKeydown}
maxlength={200}
rows={3}
Expand Down
Loading
Loading