Skip to main content

Stickies

Stickies are rich text notes with tagging, version history, and soft-delete support.

Features

Rich Text Editing

  • Markdown support
  • Code blocks with syntax highlighting
  • Inline formatting (bold, italic, links)

Version History

Every edit creates a version:

  • View previous versions
  • Restore any version
  • Compare changes

Organization

  • Tags: Organize with custom tags
  • Tag Constellation: Visual tag browser
  • Search: Full-text search across all stickies

Soft Delete

Deleted stickies go to trash:

  • Tag-based system (trash tag)
  • Restore from trash
  • Permanent delete available

Usage

Creating a Sticky

  1. Navigate to /protected/stickies
  2. Click "New Sticky"
  3. Enter content and tags
  4. Save

Tagging System

Available tags:
- work, personal, urgent
- Custom tags created on-the-fly
- Special tag: "trash" (soft delete)

Viewing Trash

The trash filter appears when deleted items exist:

  • Click "trash" tag to view deleted items
  • Restore or permanently delete

Data Model

-- Stickies table
CREATE TABLE aicync.stickies (
id UUID PRIMARY KEY,
user_id UUID REFERENCES auth.users,
content TEXT,
tags TEXT[],
deleted_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- Version history
CREATE TABLE aicync.sticky_versions (
id UUID PRIMARY KEY,
sticky_id UUID REFERENCES stickies,
content TEXT,
version_number INT,
created_at TIMESTAMPTZ DEFAULT NOW()
);

API Endpoints

MethodEndpointDescription
GET/api/stickiesList all stickies
POST/api/stickiesCreate sticky
PATCH/api/stickies/[id]Update sticky
DELETE/api/stickies/[id]Soft delete
GET/api/stickies/[id]/versionsGet versions
POST/api/stickies/[id]/restoreRestore from trash

Soft Delete Pattern

The tag-based soft delete pattern:

// Delete: Add trash tag
await supabase.from('stickies').update({
tags: [...existing, 'trash'],
deleted_at: new Date()
}).eq('id', id);

// Restore: Remove trash tag
await supabase.from('stickies').update({
tags: tags.filter(t => t !== 'trash'),
deleted_at: null
}).eq('id', id);

// Query non-deleted
const { data } = await supabase
.from('stickies')
.select()
.not('tags', 'cs', '{trash}')
.is('deleted_at', null);

Legacy Compatibility

Old items may have deleted_at set without trash tag:

// Check both for trash items
const isTrash = sticky.tags.includes('trash') ||
sticky.deleted_at !== null;