v0.1.4 2490b00

turnstile.svelte RawHistoryFolder

1<script lang="ts">
2    import { onMount } from 'svelte';
3    import { fade } from 'svelte/transition';
4    import { PUBLIC_TURNSTILE_SITE_KEY } from '$env/static/public';
5    import type { Turnstile, TurnstileOptions } from '$lib/types/turnstile';
6
7    let isVerified = $state(false);
8
9    type OnVerifyCallback = (token: string) => void;
10    let { onVerify } = $props<{ onVerify: OnVerifyCallback }>();
11    
12    let turnstileElement = $state<HTMLDivElement | null>(null);
13    let widgetId = '';
14
15    // Ensure site key is a string and trim any whitespace
16    const siteKey = String(PUBLIC_TURNSTILE_SITE_KEY).trim();
17
18    onMount(() => {
19        if (!window.turnstile) {
20            const script = document.createElement('script');
21            script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
22            script.async = true;
23            script.defer = true;
24            document.head.appendChild(script);
25
26            script.onload = initTurnstile;
27        } else {
28            initTurnstile();
29        }
30
31        return () => {
32            if (widgetId) {
33                window.turnstile?.remove(widgetId);
34            }
35        };
36    });
37
38    function initTurnstile() {
39        if (!turnstileElement || !window.turnstile) return;
40        
41        // Validate site key is a non-empty string
42        if (!siteKey) {
43            console.error('Turnstile site key is missing');
44            return;
45        }
46
47        const options: TurnstileOptions = {
48            sitekey: siteKey,
49            callback: (token: string) => {
50                if (typeof onVerify === 'function') {
51                    onVerify(token);
52                    isVerified = true;
53                } else {
54                    console.error('onVerify callback is not a function');
55                }
56            },
57            'refresh-expired': 'auto',
58            size: 'compact',
59            'error-callback': (error: unknown) => {
60                console.error('Turnstile error:', error);
61                isVerified = false; // Reset verification state on error
62            }
63        };
64
65        try {
66            widgetId = window.turnstile.render(turnstileElement, options);
67            
68            // Add resize handler
69            const handleResize = () => scaleTurnstileToFit();
70            window.addEventListener('resize', handleResize);
71            // Initial scale
72            scaleTurnstileToFit();
73            
74        } catch (error) {
75            console.error('Failed to render Turnstile:', error);
76        }
77    }
78
79    function scaleTurnstileToFit() {
80        const container = turnstileElement;
81        if (!container) return;
82
83        const iframe = container.querySelector('iframe');
84        if (!iframe) return;
85
86        const containerWidth = container.offsetWidth;
87        const scale = Math.min(1, containerWidth / 300); // 300px is approximate base width
88
89        iframe.style.transform = `scale(${scale})`;
90        iframe.style.transformOrigin = 'center center';
91    }
92</script>
93
94{#if !isVerified}
95    <div 
96        class="turnstile-wrapper" 
97        transition:fade={{ duration: 300 }}
98    >
99        <div bind:this={turnstileElement} class="turnstile-container" data-theme="light"></div>
100    </div>
101{/if}
102
103<style>
104    .turnstile-wrapper {
105        width: 100%;
106        display: flex;
107        justify-content: center;
108        align-items: center;
109        min-height: 65px; /* Approximate height for compact size */
110        overflow: hidden;
111    }
112
113    .turnstile-container {
114        display: flex;
115        justify-content: center;
116        align-items: center;
117    }
118
119    /* Handle iframe scaling */
120    :global(.turnstile-wrapper iframe) {
121        transition: transform 0.2s ease-in-out;
122    }
123</style> 

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close