The DSL already supported m~/m= tokens but the filter UI had no way to add them. Add Images/Video quick buttons and a free-text MIME input that append m~<pattern> tokens (LIKE on the type name), plus friendly token labels in dsl.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,18 @@
|
||||
tokens = [...tokens, t];
|
||||
}
|
||||
|
||||
// Free-text MIME filter. Matches against the type name (mt.name) via LIKE, so
|
||||
// "image/png" is an exact-ish match and "image/%" / "%mp4" act as patterns.
|
||||
// (m=<id> targets the numeric mime_id, which the UI doesn't expose.)
|
||||
let mimeInput = $state('');
|
||||
|
||||
function addMime() {
|
||||
const v = mimeInput.trim();
|
||||
if (!v) return;
|
||||
addToken(`m~${v}`);
|
||||
mimeInput = '';
|
||||
}
|
||||
|
||||
function removeToken(i: number) {
|
||||
tokens = tokens.filter((_, idx) => idx !== i);
|
||||
}
|
||||
@@ -197,6 +209,28 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<!-- MIME / media type — appends an m~ token like a tag/operator -->
|
||||
<div class="mime">
|
||||
<button class="token mime-token" onclick={() => addToken('m~image/%')}>Images</button>
|
||||
<button class="token mime-token" onclick={() => addToken('m~video/%')}>Video</button>
|
||||
<input
|
||||
class="mime-input"
|
||||
type="text"
|
||||
placeholder="MIME, e.g. image/png"
|
||||
bind:value={mimeInput}
|
||||
onkeydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
addMime();
|
||||
}
|
||||
}}
|
||||
autocomplete="off"
|
||||
/>
|
||||
<button class="token op-token mime-add" onclick={addMime} disabled={!mimeInput.trim()}>
|
||||
+ MIME
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Review status (mutually-exclusive r=1 / r=0) -->
|
||||
<div class="review-seg" role="group" aria-label="Review status">
|
||||
<button class="seg" class:on={reviewToken === null} onclick={() => setReview(null)}>Any</button>
|
||||
@@ -281,6 +315,47 @@
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.mime {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mime-token {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 18%, var(--color-bg-elevated));
|
||||
color: var(--color-text-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.mime-token:hover {
|
||||
background-color: color-mix(in srgb, var(--color-accent) 35%, var(--color-bg-elevated));
|
||||
}
|
||||
|
||||
.mime-input {
|
||||
flex: 1;
|
||||
min-width: 120px;
|
||||
box-sizing: border-box;
|
||||
height: 26px;
|
||||
padding: 0 8px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
|
||||
background-color: var(--color-bg-primary);
|
||||
color: var(--color-text-primary);
|
||||
font-size: 0.8rem;
|
||||
font-family: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.mime-input:focus {
|
||||
border-color: var(--color-accent);
|
||||
}
|
||||
|
||||
.mime-add:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.review-seg {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*
|
||||
* Token format (comma-separated inside braces):
|
||||
* t=<uuid> — has tag
|
||||
* m=<mime> — exact MIME
|
||||
* m~<pattern> — MIME LIKE pattern
|
||||
* m=<id> — exact MIME by numeric mime_id
|
||||
* m~<pattern> — MIME type-name LIKE pattern (e.g. image/%, image/png)
|
||||
* r=1 / r=0 — needs review / review done
|
||||
* ( ) & | ! — grouping / boolean operators
|
||||
*
|
||||
@@ -34,6 +34,10 @@ export function tokenLabel(token: string, tagNames: Map<string, string>): string
|
||||
if (token === ')') return ')';
|
||||
if (token === 'r=1') return 'Needs review';
|
||||
if (token === 'r=0') return 'Reviewed';
|
||||
if (token === 'm~image/%') return 'Images';
|
||||
if (token === 'm~video/%') return 'Video';
|
||||
if (token.startsWith('m~')) return token.slice(2);
|
||||
if (token.startsWith('m=')) return `mime #${token.slice(2)}`;
|
||||
if (token.startsWith('t=')) {
|
||||
const id = token.slice(2);
|
||||
return tagNames.get(id) ?? token;
|
||||
|
||||
Reference in New Issue
Block a user