Konsolidierter, bereinigter Stand der Wissensbasis (docs/). Frischer Wurzel-Commit, um urheberrechtlich problematische Volltexte aus der Historie zu entfernen (die bisherige Historie bestand aus einem einzigen Initial-Commit). Enthaltene Änderungen (vgl. docs/_Steuerung/CHANGELOG.md, 2026-05-29): - Copyright-Hygiene: 25 Volltext-/Übersetzungsdateien (Sharp 14 Kap., Wala 11 Kap.) entfernt; je Quelle _Fundstellen-Index.md als Provenienzbeleg; Quellnachweise + Steuerungsdateien angepasst. - Konsistenz-Korrekturen: Reichweite 000-013 (Scorecard-Regeln), Rule-ID MW-WK-DIFF-101, Quellnachweis-Dateiverweis, Dok.000 v2.0.2. - Dateinamen-Normalisierung: Startdatei ohne Leerzeichen. Originale (Wala/Sharp E-Books) privat außerhalb des Repos archiviert. Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.5 KiB
PHP
76 lines
2.5 KiB
PHP
@props([
|
|
'optionsRoute' => 'passkey.login-options',
|
|
'submitRoute' => 'passkey.login',
|
|
'label' => __('Sign in with a passkey'),
|
|
'loadingLabel' => __('Authenticating...'),
|
|
'separator' => __('Or continue with email'),
|
|
])
|
|
|
|
@assets
|
|
@vite('resources/js/passkeys.js')
|
|
@endassets
|
|
|
|
<div
|
|
x-data="{
|
|
supported: false,
|
|
loading: false,
|
|
error: null,
|
|
updateSupport() {
|
|
this.supported = Boolean(window.Passkeys?.isSupported());
|
|
},
|
|
init() {
|
|
this.updateSupport();
|
|
|
|
window.addEventListener('passkeys:ready', () => this.updateSupport(), { once: true });
|
|
},
|
|
async verify() {
|
|
this.loading = true;
|
|
this.error = null;
|
|
try {
|
|
const response = await window.Passkeys.verify({
|
|
routes: {
|
|
options: '{{ route($optionsRoute) }}',
|
|
submit: '{{ route($submitRoute) }}',
|
|
},
|
|
});
|
|
Livewire.navigate(response.redirect || '/dashboard');
|
|
} catch (e) {
|
|
if (e.constructor?.name !== 'UserCancelledError') {
|
|
this.error = e.message;
|
|
}
|
|
} finally {
|
|
this.loading = false;
|
|
}
|
|
},
|
|
}"
|
|
>
|
|
<template x-if="supported">
|
|
<div>
|
|
<div class="grid gap-2">
|
|
<flux:button
|
|
variant="outline"
|
|
icon="finger-print"
|
|
class="w-full"
|
|
x-on:click="verify()"
|
|
x-bind:disabled="loading"
|
|
>
|
|
<span x-show="!loading">{{ $label }}</span>
|
|
<span x-show="loading" x-cloak>{{ $loadingLabel }}</span>
|
|
</flux:button>
|
|
<p x-show="error" x-text="error" x-cloak
|
|
class="text-sm text-center text-red-600 dark:text-red-400"></p>
|
|
</div>
|
|
|
|
<div class="relative my-6">
|
|
<div class="absolute inset-0 flex items-center">
|
|
<div class="w-full border-t border-zinc-200 dark:border-zinc-700"></div>
|
|
</div>
|
|
<div class="relative flex justify-center text-xs uppercase">
|
|
<span class="px-2 text-zinc-500 dark:text-zinc-400 bg-white dark:bg-zinc-900">
|
|
{{ $separator }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</div>
|