Example-content: footer button creates 20 realistic demo cards in a "Project Alpha" bucket for screenshots and first-time exploration. Website: full-page sections with large watermark labels, screenshot hero with typing animation, email/spam section, Gitea detail, relay encryption flow, keyboard keycaps, deploy options. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
488 lines
13 KiB
JavaScript
488 lines
13 KiB
JavaScript
// ── Example content plugin — one-click demo data ──
|
|
|
|
const h = window.hyprcrd;
|
|
const api = h?.api || window._exampleApi;
|
|
|
|
const COLORS = {
|
|
blue: '#38bdf8', purple: '#a78bfa', pink: '#f472b6', red: '#fb7185',
|
|
green: '#34d399', yellow: '#fbbf24', orange: '#fb923c', slate: '#64748b',
|
|
};
|
|
|
|
const CARDS = [
|
|
{
|
|
title: 'Architecture RFC',
|
|
color: COLORS.blue,
|
|
body: `## System Architecture
|
|
|
|
The new microservice layout separates auth, data, and notification layers.
|
|
|
|
### Key decisions
|
|
- PostgreSQL for persistent state
|
|
- Redis for session cache + pub/sub
|
|
- gRPC between internal services
|
|
- REST + WebSocket for client API
|
|
|
|
### Open questions
|
|
- [ ] Rate limiting strategy
|
|
- [ ] Multi-region failover
|
|
- [x] Auth token rotation scheme
|
|
- [x] Database migration tooling
|
|
|
|
See also: [[Deploy checklist]]
|
|
|
|
#project/api #architecture`,
|
|
},
|
|
{
|
|
title: 'Sprint Planning Q3',
|
|
color: COLORS.purple,
|
|
body: `## Goals
|
|
|
|
1. Finish API v3 migration
|
|
2. Launch mobile auth flow
|
|
3. Load testing at 10k concurrent
|
|
4. Cut staging→prod deploy time to <5min
|
|
|
|
**Deadline:** 2026-07-01
|
|
**Owner:** @alice
|
|
|
|
### Capacity
|
|
| Team | Points | Allocated |
|
|
|----------|--------|-----------|
|
|
| Backend | 34 | 30 |
|
|
| Frontend | 21 | 18 |
|
|
| Infra | 13 | 13 |
|
|
|
|
#meeting #project`,
|
|
},
|
|
{
|
|
title: 'Auth timeout on mobile',
|
|
color: COLORS.red,
|
|
body: `Users on iOS Safari report being logged out after ~3 minutes of inactivity.
|
|
|
|
### Repro
|
|
1. Login on iPhone 15 (Safari 18)
|
|
2. Switch to another app for 3 min
|
|
3. Return → "session expired"
|
|
|
|
### Investigation
|
|
- Cookie \`SameSite=Strict\` might be too aggressive
|
|
- Safari's ITP may be purging the session cookie
|
|
- Backend session TTL is 24h — not the issue
|
|
|
|
### Fix
|
|
Try \`SameSite=Lax\` + short-lived refresh token.
|
|
|
|
#bug #urgent #project/frontend/auth`,
|
|
},
|
|
{
|
|
title: 'Deploy checklist',
|
|
color: COLORS.green,
|
|
body: `- [x] Run full test suite
|
|
- [x] Update CHANGELOG.md
|
|
- [x] Bump version in config
|
|
- [ ] Tag release in git
|
|
- [ ] Build Docker image
|
|
- [ ] Push to registry
|
|
- [ ] Roll out to staging
|
|
- [ ] Smoke test staging
|
|
- [ ] Promote to production
|
|
- [ ] Monitor Grafana for 30min
|
|
|
|
> Last deploy: 2026-04-08 by @bob
|
|
> Rollback: \`git revert HEAD && ./deploy.sh\`
|
|
|
|
#ops #deploy`,
|
|
},
|
|
{
|
|
title: 'Meeting 2026-04-15 — Retro',
|
|
color: COLORS.yellow,
|
|
due_date: '2026-04-15',
|
|
body: `**Zeit:** 14:00-15:00
|
|
**Ort:** Room 42
|
|
**Teilnehmer:** Alice, Bob, Carol
|
|
|
|
---
|
|
|
|
### What went well
|
|
- Deploy frequency improved (3x/week → daily)
|
|
- Zero P1 incidents this sprint
|
|
- New search feature got good feedback
|
|
|
|
### What to improve
|
|
- PR review turnaround still >24h
|
|
- Flaky integration tests causing false alarms
|
|
- Documentation gaps in relay module
|
|
|
|
### Action items
|
|
- [ ] @alice: set up PR review SLA
|
|
- [ ] @bob: fix top 5 flaky tests
|
|
- [ ] @carol: write relay onboarding doc
|
|
|
|
#meeting #termin #retro`,
|
|
},
|
|
{
|
|
title: 'Reading list',
|
|
color: COLORS.slate,
|
|
body: `Papers and articles to read:
|
|
|
|
- [Designing Data-Intensive Applications](https://dataintensive.net/) — Ch. 7-9
|
|
- [The Twelve-Factor App](https://12factor.net/)
|
|
- \`CAP theorem revisited\` — Brewer 2012
|
|
- Local-first software — Kleppmann et al.
|
|
|
|
### Notes
|
|
> "The truth is the log. The database is a cache."
|
|
> — Pat Helland
|
|
|
|
#personal #research`,
|
|
},
|
|
{
|
|
title: 'API v3 migration',
|
|
color: COLORS.blue,
|
|
body: `### Endpoints to migrate
|
|
|
|
| v2 | v3 | Status |
|
|
|----|-----|--------|
|
|
| GET /users | GET /api/v3/users | done |
|
|
| POST /auth | POST /api/v3/auth/token | done |
|
|
| GET /cards | GET /api/v3/cards | in progress |
|
|
| WS /stream | WS /api/v3/events | planned |
|
|
|
|
### Breaking changes
|
|
- Auth header: \`Bearer\` instead of \`X-Token\`
|
|
- Pagination: cursor-based instead of offset
|
|
- Error format: \`{"error": {"code": ..., "message": ...}}\`
|
|
|
|
[[Architecture RFC]]
|
|
|
|
#project/api #migration`,
|
|
},
|
|
{
|
|
title: 'Infra cost review',
|
|
color: COLORS.orange,
|
|
body: `### Monthly breakdown (April)
|
|
|
|
\`\`\`
|
|
Compute (k8s) €340 ████████░░ 68%
|
|
Storage (S3) €85 ██░░░░░░░░ 17%
|
|
Database (RDS) €52 █░░░░░░░░░ 10%
|
|
CDN + DNS €18 ░░░░░░░░░░ 4%
|
|
Monitoring €8 ░░░░░░░░░░ 2%
|
|
─────────────────────────────────────
|
|
Total €503
|
|
\`\`\`
|
|
|
|
Budget: €600 → **€97 headroom**
|
|
|
|
Next action: evaluate spot instances for batch workers.
|
|
|
|
#ops #infra #finance`,
|
|
},
|
|
{
|
|
title: 'Birthday Alice',
|
|
color: COLORS.pink,
|
|
due_date: '2026-06-12',
|
|
body: `#geburtstag`,
|
|
},
|
|
{
|
|
title: 'Onboarding: new developer',
|
|
color: COLORS.green,
|
|
body: `## First week checklist
|
|
|
|
- [ ] Laptop + accounts provisioned
|
|
- [ ] SSH key added to Gitea
|
|
- [ ] Access to staging cluster
|
|
- [x] Slack channels joined
|
|
- [x] 1:1 with team lead
|
|
- [ ] Read [[Architecture RFC]]
|
|
- [ ] First PR: fix a good-first-issue
|
|
|
|
### Contacts
|
|
| Role | Person |
|
|
|------|--------|
|
|
| Buddy | @carol |
|
|
| Team lead | @alice |
|
|
| Infra | @dave |
|
|
|
|
#onboarding #ops`,
|
|
},
|
|
{
|
|
title: 'Competitive analysis',
|
|
color: COLORS.purple,
|
|
body: `### Landscape
|
|
|
|
| Product | Self-hosted | Offline | E2E encryption |
|
|
|---------|:-----------:|:-------:|:--------------:|
|
|
| Notion | no | no | no |
|
|
| Obsidian| yes | yes | paid |
|
|
| Logseq | yes | yes | no |
|
|
| **hyprcrd** | **yes** | **yes** | **yes** |
|
|
|
|
### Differentiators
|
|
- Tag extraction from body (zero-friction categorization)
|
|
- Relay model: multi-tenant sync without cloud dependency
|
|
- Plugin system: extensible without forking
|
|
- CalDAV native (not just ical export)
|
|
|
|
#research #strategy`,
|
|
},
|
|
{
|
|
title: 'Security audit notes',
|
|
color: COLORS.red,
|
|
body: `## Audit 2026-Q1 findings
|
|
|
|
### Critical
|
|
~~1. Session token in URL parameter~~ → fixed in v1.2.1
|
|
|
|
### High
|
|
2. CSRF token not rotated on privilege change → [[Auth timeout on mobile]]
|
|
|
|
### Medium
|
|
3. Upload directory allows .svg (XSS vector)
|
|
4. Rate limiter not applied to /api/export
|
|
|
|
### Low
|
|
5. Server version exposed in HTTP headers
|
|
6. No CSP header on main app
|
|
|
|
> Next audit scheduled: 2026-07-15
|
|
|
|
#security #audit #urgent`,
|
|
},
|
|
{
|
|
title: 'Design system tokens',
|
|
color: COLORS.purple,
|
|
body: `### Color palette
|
|
|
|
\`\`\`
|
|
--t-bg: #0f172a base background
|
|
--t-surface: #1e293b card/panel surface
|
|
--t-border: #334155 borders
|
|
--t-text: #94a3b8 body text
|
|
--t-bright: #e2e8f0 headlines
|
|
--t-accent: #38bdf8 primary accent
|
|
--t-accent2: #a78bfa secondary accent
|
|
--t-danger: #fb7185 errors/destructive
|
|
\`\`\`
|
|
|
|
### Typography
|
|
- **UI:** Silkscreen (pixel font)
|
|
- **Content:** JetBrains Mono
|
|
- **Sizes:** 9px labels, 11px body, 13px headings
|
|
|
|
### Radius scale
|
|
\`--ui-radius-card\`, \`--ui-radius-modal\`, \`--ui-radius-input\`
|
|
|
|
All overridable per theme preset.
|
|
|
|
#design #frontend`,
|
|
},
|
|
{
|
|
title: 'Customer feedback digest',
|
|
color: COLORS.yellow,
|
|
body: `### Week of 2026-04-07
|
|
|
|
**Positive:**
|
|
- "Search is blazing fast" — 3 mentions
|
|
- "CalDAV sync just works" — 2 mentions
|
|
- "Love the keyboard shortcuts" — @user42
|
|
|
|
**Requests:**
|
|
- Mobile app (→ Electron PWA?)
|
|
- Shared boards without relay (→ collab-buckets?)
|
|
- Dark mode for relay UI (→ already shipped!)
|
|
|
|
**Issues:**
|
|
- Image upload fails >5MB on shared hosting
|
|
- Calendar popup overlaps on 13" screens
|
|
|
|
#feedback #product`,
|
|
},
|
|
{
|
|
title: 'Database schema notes',
|
|
color: COLORS.slate,
|
|
body: `### Core tables
|
|
|
|
\`\`\`sql
|
|
cards (id, title, body, color, tags,
|
|
bucket_id, parent_id, position,
|
|
due_date, relay_uid,
|
|
created_at, updated_at)
|
|
|
|
buckets (id, name, color, parent_id,
|
|
collab_uid, position)
|
|
|
|
settings (key, value)
|
|
\`\`\`
|
|
|
|
### Indexes
|
|
- \`cards.bucket_id\` — bucket filter
|
|
- \`cards.parent_id\` — group lookup
|
|
- \`buckets.collab_uid UNIQUE\` — relay sync
|
|
|
|
SQLite WAL mode for concurrent reads.
|
|
|
|
#architecture #database`,
|
|
},
|
|
{
|
|
title: 'Release notes v1.3',
|
|
color: COLORS.green,
|
|
body: `## What's new
|
|
|
|
### Features
|
|
- **Tooltip system** — custom styled tooltips replace all native titles
|
|
- **Asset picker** — thumbnail grid in the editor toolbar
|
|
- **Super-bucket split** — click activates, caret opens dropdown
|
|
- **Watch plugin** — analog clock overlay with SVG watchface
|
|
- **Card peek** — hover preview with rendered markdown
|
|
|
|
### Fixes
|
|
- Shortcuts no longer fire when typing in inputs
|
|
- Gallery opens even if API fetch fails
|
|
- Relay poll suppressed during user actions
|
|
- Find/replace integrated into search overlay
|
|
|
|
### Breaking
|
|
- All \`title=""\` attributes migrated to \`data-tip=""\`
|
|
|
|
#release #changelog`,
|
|
},
|
|
{
|
|
title: '2026-04-12',
|
|
color: COLORS.slate,
|
|
body: `## Journal
|
|
|
|
Started the day reviewing PRs. Merged the tooltip system and the asset picker.
|
|
|
|
Spent the afternoon on the Electron build — static PHP binary works but the WAL/SHM files caused issues. Fixed by deleting stale lock files.
|
|
|
|
Had a good session on the watch plugin. The wobble animation on the second hand is a nice touch.
|
|
|
|
**Tomorrow:**
|
|
- [ ] Test relay collab-buckets with two instances
|
|
- [ ] Write release notes
|
|
- [ ] Screenshot for website
|
|
|
|
#journal`,
|
|
},
|
|
{
|
|
title: 'Backup strategy',
|
|
color: COLORS.orange,
|
|
body: `### Current
|
|
|
|
Daily cron:
|
|
\`\`\`bash
|
|
#!/bin/bash
|
|
DATE=$(date +%Y-%m-%d)
|
|
cp data/hyprcrd.db "/backup/hyprcrd-$DATE.db"
|
|
tar czf "/backup/uploads-$DATE.tar.gz" public/uploads/
|
|
find /backup -mtime +30 -delete
|
|
\`\`\`
|
|
|
|
### Planned
|
|
- [ ] Automated export ZIP via API
|
|
- [ ] Offsite copy to S3-compatible storage
|
|
- [ ] Test restore monthly
|
|
- [ ] Document in runbook
|
|
|
|
> WAL checkpoint before copy:
|
|
> \`sqlite3 data/hyprcrd.db "PRAGMA wal_checkpoint(TRUNCATE);"\`
|
|
|
|
#ops #backup #infra`,
|
|
},
|
|
{
|
|
title: 'Plugin ideas',
|
|
color: COLORS.pink,
|
|
body: `### Brainstorm
|
|
|
|
- **Kanban columns** — cards as swimlanes per bucket
|
|
- **Time tracking** — start/stop timer per card, weekly report
|
|
- **AI summary** — auto-generate card summaries with local LLM
|
|
- **RSS reader** — fetch feeds into cards
|
|
- **Webhook triggers** — POST on card create/update/delete
|
|
- **PDF export** — render card as styled PDF
|
|
- **Voice memo** — record audio, attach to card, transcribe
|
|
|
|
### Criteria for inclusion
|
|
1. Does it help *find* information?
|
|
2. Can it be a drop-in plugin (no core changes)?
|
|
3. Is it useful without an internet connection?
|
|
|
|
#idea #plugins #brainstorm`,
|
|
},
|
|
{
|
|
title: 'Monitoring setup',
|
|
color: COLORS.green,
|
|
body: `### Stack
|
|
|
|
- **Uptime:** UptimeRobot (free tier, 5min interval)
|
|
- **Errors:** Sentry self-hosted → \`sentry.internal\`
|
|
- **Metrics:** Grafana + Prometheus
|
|
- **Logs:** Loki + promtail on each node
|
|
|
|
### Alerts
|
|
| Metric | Threshold | Channel |
|
|
|--------|-----------|---------|
|
|
| HTTP 5xx rate | >1% for 5min | Slack #ops-alerts |
|
|
| Response time p95 | >2s for 10min | Slack #ops-alerts |
|
|
| Disk usage | >85% | Email + Slack |
|
|
| SSL expiry | <14 days | Email |
|
|
|
|
Dashboard: \`grafana.internal/d/hyprcrd\`
|
|
|
|
#ops #monitoring #infra`,
|
|
},
|
|
];
|
|
|
|
async function generate() {
|
|
const bucket = await api('api/buckets', { method: 'POST', body: { name: 'Project Alpha' } });
|
|
const bid = bucket?.id;
|
|
if (!bid) return false;
|
|
await api(`api/buckets/${bid}`, { method: 'PATCH', body: { color: COLORS.blue } });
|
|
|
|
for (const c of CARDS) {
|
|
await api('api/cards', {
|
|
method: 'POST',
|
|
body: { title: c.title, body: c.body, color: c.color, bucket_id: bid, due_date: c.due_date || null },
|
|
});
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function init() {
|
|
const footer = document.getElementById('main-footer');
|
|
if (!footer) return;
|
|
|
|
const btn = document.createElement('button');
|
|
btn.className = 'flex items-center gap-1.5 px-2.5 py-1 text-xs border border-t-border text-t-muted hover:border-t-accent hover:text-t-accent transition-colors';
|
|
btn.innerHTML = '<i class="ph ph-flask text-sm"></i><span class="font-pixel text-[10px]">Demo</span>';
|
|
btn.dataset.tip = 'Create example bucket with demo cards';
|
|
|
|
btn.addEventListener('click', async () => {
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="ph ph-spinner text-sm"></i><span class="font-pixel text-[10px]">creating...</span>';
|
|
const ok = await generate();
|
|
if (ok) {
|
|
btn.innerHTML = '<i class="ph ph-check text-sm"></i><span class="font-pixel text-[10px]">Done</span>';
|
|
await window._reloadAll?.();
|
|
} else {
|
|
btn.innerHTML = '<i class="ph ph-x text-sm"></i><span class="font-pixel text-[10px]">Failed</span>';
|
|
}
|
|
setTimeout(() => {
|
|
btn.innerHTML = '<i class="ph ph-flask text-sm"></i><span class="font-pixel text-[10px]">Demo</span>';
|
|
btn.disabled = false;
|
|
}, 2000);
|
|
});
|
|
|
|
const firstChild = footer.querySelector('div');
|
|
if (firstChild) firstChild.appendChild(btn);
|
|
|
|
h?.registerPlugin({ name: 'example-content', version: '1.0.0' });
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|