Overview
Teak provides multiple organization strategies: favorites for quick access, tags for categorization, type filters for browsing, and visual filters for aesthetic discovery.
Favorites
Mark important cards for instant access.
Marking as Favorite
import { api } from "@teak/convex";
import { useMutation } from "convex/react";
const updateCard = useMutation(api.cards.updateCard);
await updateCard({
cardId: "abc123",
isFavorited: true
});
Querying Favorites
import { api } from "@teak/convex";
import { useQuery } from "convex/react";
const favorites = useQuery(api.card.getCards.getCards, {
favoritesOnly: true,
limit: 50
});
const { results, loadMore, status } = usePaginatedQuery(
api.card.getCards.searchCardsPaginated,
{
favoritesOnly: true
},
{ initialNumItems: 50 }
);
Favorites use the optimized by_user_favorites_deleted index for fast queries.
Organize cards with custom labels.
Teak maintains two separate tag systems:
| Feature | User Tags | AI Tags |
|---|
| Source | Manual input | AI-generated |
| Field | tags | aiTags |
| Editable | Yes | No (regenerate card) |
| Search | Full-text search | Full-text search |
// At creation
await createCard({
content: "Design inspiration",
tags: ["ui", "dark-mode", "inspiration"]
});
// Update existing card
await updateCard({
cardId: "abc123",
tags: ["ui", "dark-mode", "inspiration", "new-tag"]
});
Tags are searchable via the full-text search system:
const results = useQuery(api.card.getCards.searchCards, {
searchQuery: "dark-mode", // Matches tags containing "dark-mode"
limit: 50
});
Tag searches use partial matching. Searching “dark” will find cards tagged “dark-mode”.
Type Filters
Filter by card type to focus on specific content.
Single Type
const images = useQuery(api.card.getCards.searchCards, {
types: ["image"],
limit: 50
});
Single-type filters use the optimized by_user_type_deleted index.
Multiple Types
const media = useQuery(api.card.getCards.searchCards, {
types: ["image", "video", "audio"],
limit: 50
});
All Available Types
import { CARD_TYPES } from "@teak/convex/shared/constants";
// ["text", "link", "image", "video", "audio", "document", "palette", "quote"]
console.log(CARD_TYPES);
Visual Filters
Discover cards by aesthetic style and color.
Visual Styles
Teak extracts 13 visual style facets from images and videos:
Tab Title
Tab Title
Tab Title
| Style | Description |
|---|
| abstract | Abstract art, non-representational |
| cinematic | Film-like, dramatic composition |
| dark | Dark mode, low-key lighting |
| illustrative | Hand-drawn, illustrated |
| minimal | Minimalist, simple composition |
| monochrome | Black and white, grayscale |
| moody | Atmospheric, dramatic |
| pastel | Soft, muted colors |
| photographic | Realistic, photo-like |
| retro | Vintage computing, synthwave |
| surreal | Dreamlike, surreal |
| vintage | Aged, antique aesthetic |
| vibrant | Highly saturated, colorful |
Teak normalizes similar terms:"black and white" → "monochrome"
"b&w" → "monochrome"
"photo" → "photographic"
"colorful" → "vibrant"
"drawing" → "illustrative"
"minimalist" → "minimal"
See packages/convex/shared/constants.ts:54-99 for all aliases. const moodyImages = useQuery(api.card.getCards.searchCards, {
types: ["image"],
styleFilters: ["moody", "cinematic"],
limit: 50
});
Color Hues
Filter by dominant color hue:
Tab Title
Tab Title
Tab Title
| Hue | Coverage |
|---|
| red | Red tones |
| orange | Orange tones |
| yellow | Yellow tones |
| green | Green tones |
| teal | Teal tones |
| cyan | Cyan tones |
| blue | Blue tones |
| purple | Purple, violet, indigo |
| pink | Pink, magenta, fuchsia |
| brown | Brown tones |
| neutral | Gray, monochrome |
"violet" → "purple"
"indigo" → "purple"
"magenta" → "pink"
"gray" → "neutral"
"grey" → "neutral"
const purpleImages = useQuery(api.card.getCards.searchCards, {
types: ["image"],
hueFilters: ["purple", "pink"],
limit: 50
});
Exact Colors
Filter by specific hex codes:
const results = useQuery(api.card.getCards.searchCards, {
hexFilters: ["#FF6B35", "#F7931E"],
limit: 50
});
Hex filters match palette cards containing those exact colors.
Combined Visual Filters
const aestheticImages = useQuery(api.card.getCards.searchCards, {
types: ["image"],
styleFilters: ["minimal", "monochrome"],
hueFilters: ["neutral"],
limit: 50
});
Visual filters use dedicated facet indexes for performance. See packages/convex/card/visualFilters.ts.
Time Ranges
Filter by creation date:
const recentCards = useQuery(api.card.getCards.searchCards, {
createdAtRange: {
start: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
end: Date.now()
},
limit: 50
});
Time range queries use the by_created index for efficient filtering.
Trash
Cards can be soft-deleted and recovered:
Move to Trash
await updateCard({
cardId: "abc123",
isDeleted: true
});
View Trash
const trashedCards = useQuery(api.card.getCards.searchCards, {
showTrashOnly: true,
limit: 50
});
Restore from Trash
await updateCard({
cardId: "abc123",
isDeleted: false // or undefined
});
Permanent deletion is not yet implemented. All deletions are soft-deletes.
Combining Filters
All filters can be combined for powerful queries:
const results = useQuery(api.card.getCards.searchCards, {
searchQuery: "design inspiration",
types: ["image", "link"],
favoritesOnly: true,
styleFilters: ["minimal", "vibrant"],
hueFilters: ["blue", "purple"],
createdAtRange: {
start: Date.now() - 30 * 24 * 60 * 60 * 1000,
end: Date.now()
},
limit: 50
});
Use Specific Indexes
Single-type and favorites-only queries use optimized compound indexes:
by_user_type_deleted for single type
by_user_favorites_deleted for favorites
by_created for time ranges
Paginate Large Results
const { results, loadMore, status } = usePaginatedQuery(
api.card.getCards.searchCardsPaginated,
{ /* filters */ },
{ initialNumItems: 50 }
);
Visual Filters with Type Filter
Combine visual filters with type filters to reduce search space:{
types: ["image"], // Narrow down first
styleFilters: ["minimal"]
}
Source Reference
- Query logic:
packages/convex/card/getCards.ts:45-747
- Visual filters:
packages/convex/card/visualFilters.ts
- Constants:
packages/convex/shared/constants.ts:20-227