- JWT revocation with JTI (UUID v4): in-memory tracking, POST /api/logout, refresh handler revokes old JTI, RequireAuth rejects revoked tokens - Fix extractKey for S3 portfolio deletion: extracts full key path from URLs instead of just filename, preventing orphaned storage files - Notes validation: max=1000000 on all 13 Notes fields across 4 booking structs - CharCounter: grapheme-aware counter (Intl.Segmenter), threshold 750K, color-coded, integrated into 6 booking/admin components - loginInProgress: timestamp-based tracking, 30s staleness, 20-entry cap (429), ticker cleanup for stuck entries - Profile picture 15MB client-side limit, portfolio 20MB backend limit - Exceptional scheduling: expand query start to Monday of week - TodayCalendar: week-range fetching, closing time indicator, short-day lunch skip - NavBar: link reorder, mobile burger badge, slide transition, backdrop - ImageUpload: 20MB limit with visual feedback - formatDateISO: shared YYYY-MM-DD utility, shouldApplyLunchProtection helper - Update README.md and all Obsidian docs (Overview, Technical, Admin, Future Work) - Add 28 new tests: JWT (11), auth handlers (7), portfolio extractKey (5), notes validation (5). go build + go vet clean with test,dev tags
28 lines
908 B
Svelte
28 lines
908 B
Svelte
<script lang="ts">
|
|
type Props = {
|
|
text: string;
|
|
maxChars?: number;
|
|
threshold?: number; // when to start showing counter
|
|
};
|
|
let { text, maxChars = 1000000, threshold = 750000 }: Props = $props();
|
|
|
|
const graphemeCount = $derived.by(() => {
|
|
const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' });
|
|
return [...segmenter.segment(text)].length;
|
|
});
|
|
|
|
const shouldShow = $derived(graphemeCount > threshold);
|
|
const remaining = $derived(maxChars - graphemeCount);
|
|
const color = $derived(
|
|
graphemeCount > 950000 ? 'text-red-600' :
|
|
graphemeCount > 800000 ? 'text-yellow-600' :
|
|
'text-green-600'
|
|
);
|
|
</script>
|
|
|
|
{#if shouldShow}
|
|
<p class="text-xs {color}">
|
|
{remaining.toLocaleString()} characters remaining ({graphemeCount.toLocaleString()} / {maxChars.toLocaleString()})
|
|
</p>
|
|
{/if}
|