How chip-based search works
Most note apps give you a search bar that filters by title or full-text. Glifo uses a different model: structured filter chips that compose into complex queries. Type #blog, press Enter, and you get a persistent chip — not a text search that disappears when you clear the input.
The chip model
Each chip is a typed filter with a specific behavior:
| Prefix | Type | Behavior |
|---|---|---|
#tag | Tag include | Notes with this tag |
-#tag | Tag exclude | Notes without this tag |
/folder | Folder scope | Notes in this folder |
modified:week | Date filter | Modified within timeframe |
is:untagged | Status | Notes matching a condition |
has:code | Content | Notes containing specific content |
type:drawing | Type | Filter by item type |
Chips are parsed from the search input when you press Enter. The prefix determines the chip type, and the rest becomes the value.
Composition rules
Multiple chips combine with AND logic:
#blog -#draft modified:month
This means: notes tagged "blog", NOT tagged "draft", modified this month.
Within a single tag chip, multiple values combine with OR:
#blog,tutorial
This means: notes tagged "blog" OR "tutorial".
The distinction matters. AND between chips narrows results. OR within a chip widens them. Together, they let you express queries like "recent blog posts or tutorials, excluding drafts" without a query language.
Saved views
Any combination of chips can be saved as a named view. Views persist across sessions and appear in the sidebar. This turns a complex filter like #project /Work -#archived modified:month into a single click.
The implementation
The search engine runs entirely on the frontend (part of the React layer). Each chip type maps to a filter function:
function matchesChip(note: Note, chip: SearchChip): boolean {
switch (chip.type) {
case 'tag':
return chip.values.some(v => note.tags.includes(v));
case 'tag-exclude':
return !chip.values.some(v => note.tags.includes(v));
case 'folder':
return note.folder === chip.value;
case 'modified':
return isWithinDateRange(note.modified, chip.value);
// ...
}
}
Notes pass through all chips sequentially. If any chip rejects a note, it's filtered out. This makes the system both simple to implement and predictable to use.
Free text
If you type something that doesn't match a chip prefix, it becomes a free-text filter — matching against note titles and content. Free text chips also compose with AND, so you can combine structured filters with keyword search.
The chip model works because it mirrors how people actually think about their notes: "my blog posts from this month" maps directly to #blog modified:month. No boolean operators to remember, no query syntax to learn. See keyboard shortcuts for the full list of search operators.