@circuitwall/jarela 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js +10 -1
- package/.next/standalone/.next/server/app/api/v1/builtin-tools/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js +10 -5
- package/.next/standalone/.next/server/app/api/v1/dashboard/currency/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js +9 -1
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js.map +1 -1
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +33 -8
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js.map +1 -1
- package/.next/standalone/.next/server/app/page.js +63 -202
- package/.next/standalone/.next/server/app/page.js.map +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup/page.js +1 -1
- package/.next/standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/1718.js +159 -0
- package/.next/standalone/.next/server/chunks/1718.js.map +1 -0
- package/.next/standalone/.next/server/chunks/2082.js +6 -3
- package/.next/standalone/.next/server/chunks/2082.js.map +1 -1
- package/.next/standalone/.next/server/chunks/210.js +28 -0
- package/.next/standalone/.next/server/chunks/210.js.map +1 -1
- package/.next/standalone/.next/server/chunks/423.js +6 -3
- package/.next/standalone/.next/server/chunks/423.js.map +1 -1
- package/.next/standalone/.next/server/chunks/4631.js +37 -5
- package/.next/standalone/.next/server/chunks/4631.js.map +1 -1
- package/.next/standalone/.next/server/chunks/8167.js +255 -204
- package/.next/standalone/.next/server/chunks/8167.js.map +1 -1
- package/.next/standalone/.next/server/chunks/8866.js +38 -5
- package/.next/standalone/.next/server/chunks/8866.js.map +1 -1
- package/.next/standalone/.next/server/chunks/9032.js +8 -0
- package/.next/standalone/.next/server/chunks/9032.js.map +1 -1
- package/.next/standalone/.next/server/chunks/{7883.js → 9557.js} +15 -3
- package/.next/standalone/.next/server/chunks/9557.js.map +1 -0
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/middleware.js +6 -3
- package/.next/standalone/.next/server/pages/404.html +2 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/proxy.js.map +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/{2351-68d8987bbe17ba2d.js → 2351-1ab119fb3b48f4c9.js} +258 -205
- package/.next/standalone/.next/static/chunks/2351-1ab119fb3b48f4c9.js.map +1 -0
- package/.next/standalone/.next/static/chunks/{9209-0d46118e502f8bf5.js → 4097-64691f9110cf167c.js} +14 -2
- package/.next/standalone/.next/static/chunks/4097-64691f9110cf167c.js.map +1 -0
- package/.next/standalone/.next/static/chunks/app/{page-74846c864241b96d.js → page-145150e0468544e7.js} +64 -203
- package/.next/standalone/.next/static/chunks/app/page-145150e0468544e7.js.map +1 -0
- package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js → page-a1463a9ace439ff7.js} +2 -2
- package/.next/standalone/.next/static/chunks/app/setup/{page-9a465b5fa755b3c3.js.map → page-a1463a9ace439ff7.js.map} +1 -1
- package/.next/standalone/.next/static/chunks/{webpack-ff5627013a5e3842.js → webpack-f4ac5c5f92cfd1c1.js} +13 -1
- package/.next/standalone/.next/static/chunks/webpack-f4ac5c5f92cfd1c1.js.map +1 -0
- package/.next/standalone/package.json +1 -1
- package/CHANGELOG.md +60 -0
- package/README.md +1 -1
- package/api/client.ts +10 -9
- package/app/api/v1/dashboard/currency/route.ts +7 -2
- package/app/api/v1/providers/[provider]/probe/route.ts +12 -1
- package/app/api/v1/threads/[thread_id]/run/route.ts +22 -8
- package/components/layout/AppShell.tsx +53 -17
- package/components/setup/PinKeypad.tsx +238 -0
- package/components/setup/ScreenLock.tsx +8 -173
- package/components/setup/UnlockScreen.tsx +25 -192
- package/lib/documents/remote/github.ts +16 -2
- package/lib/documents/remote/mail.ts +11 -2
- package/lib/lifecycle/shutdown.ts +9 -0
- package/lib/providers/github-copilot-auth.ts +2 -0
- package/lib/providers/github-copilot.ts +1 -0
- package/lib/tools/async-results.ts +11 -0
- package/package.json +1 -1
- package/scripts/install-to-system.ps1 +2 -2
- package/scripts/installed-launcher.ps1 +81 -17
- package/.next/standalone/.next/server/chunks/7883.js.map +0 -1
- package/.next/standalone/.next/static/chunks/2351-68d8987bbe17ba2d.js.map +0 -1
- package/.next/standalone/.next/static/chunks/9209-0d46118e502f8bf5.js.map +0 -1
- package/.next/standalone/.next/static/chunks/app/page-74846c864241b96d.js.map +0 -1
- package/.next/standalone/.next/static/chunks/webpack-ff5627013a5e3842.js.map +0 -1
- /package/.next/standalone/.next/static/{AV5AO0yTRABo-NgwxhDe7 → WQdcnm9NyqpeNc0Z8_woo}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{AV5AO0yTRABo-NgwxhDe7 → WQdcnm9NyqpeNc0Z8_woo}/_ssgManifest.js +0 -0
|
@@ -141,14 +141,14 @@ async function request(path, init) {
|
|
|
141
141
|
signal: timeoutCtrl.signal
|
|
142
142
|
});
|
|
143
143
|
if (!res.ok) {
|
|
144
|
-
// 423
|
|
145
|
-
//
|
|
146
|
-
// sees the failure
|
|
147
|
-
// going to clear on its own.
|
|
144
|
+
// 423 lock states: distinct events so AppShell can mount the
|
|
145
|
+
// right overlay (decrypt vs presence-check). Both throw so the
|
|
146
|
+
// caller still sees the failure — no point retrying, the lock
|
|
147
|
+
// isn't going to clear on its own.
|
|
148
148
|
if (res.status === 423) {
|
|
149
149
|
const cloned = res.clone();
|
|
150
150
|
const body = await cloned.json().catch(()=>null);
|
|
151
|
-
if (
|
|
151
|
+
if (false) {}
|
|
152
152
|
throw new Error(`423 ${body?.error ?? "locked"}`);
|
|
153
153
|
}
|
|
154
154
|
const text = await res.text().catch(()=>res.statusText);
|
|
@@ -926,210 +926,26 @@ const api = {
|
|
|
926
926
|
/* harmony export */ });
|
|
927
927
|
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(48249);
|
|
928
928
|
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__);
|
|
929
|
-
/* harmony import */ var
|
|
930
|
-
/* harmony import */ var
|
|
931
|
-
/* harmony import */ var
|
|
929
|
+
/* harmony import */ var next_navigation__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(19099);
|
|
930
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(67484);
|
|
931
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_2__);
|
|
932
|
+
/* harmony import */ var _PinKeypad__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(71654);
|
|
932
933
|
/* __next_internal_client_entry_do_not_use__ UnlockScreen auto */
|
|
933
934
|
|
|
934
935
|
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
const [digits, setDigits] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
|
|
942
|
-
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
|
|
943
|
-
const [submitting, setSubmitting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
|
|
944
|
-
const [retryAfterSec, setRetryAfterSec] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(0);
|
|
945
|
-
const containerRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
|
|
946
|
-
// Hard guard against parallel submits. setState updaters can run more
|
|
947
|
-
// than once (dev StrictMode, concurrent rendering), so if `submit`
|
|
948
|
-
// were called from inside `setDigits` we'd POST twice and the second
|
|
949
|
-
// request would race the first into `unlockMasterKey()` after state
|
|
950
|
-
// already flipped to unlocked - the route would 500.
|
|
951
|
-
const submittingRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(false);
|
|
952
|
-
const submit = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(async (pin)=>{
|
|
953
|
-
if (submittingRef.current) return;
|
|
954
|
-
submittingRef.current = true;
|
|
955
|
-
setSubmitting(true);
|
|
956
|
-
setError(null);
|
|
957
|
-
try {
|
|
958
|
-
const res = await fetch("/api/v1/security/unlock", {
|
|
959
|
-
method: "POST",
|
|
960
|
-
headers: {
|
|
961
|
-
"content-type": "application/json"
|
|
962
|
-
},
|
|
963
|
-
body: JSON.stringify({
|
|
964
|
-
pin
|
|
965
|
-
})
|
|
966
|
-
});
|
|
967
|
-
if (res.ok) {
|
|
968
|
-
window.location.reload();
|
|
969
|
-
return;
|
|
970
|
-
}
|
|
971
|
-
const body = await res.json().catch(()=>({}));
|
|
972
|
-
if (res.status === 409 && body.error === "not-locked") {
|
|
973
|
-
// Master key was already unlocked (host typed the PIN, or
|
|
974
|
-
// another tab beat us to it). The goal state is reached —
|
|
975
|
-
// reload into the app shell.
|
|
976
|
-
window.location.reload();
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
if (res.status === 429 && typeof body.retry_after_ms === "number") {
|
|
980
|
-
setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
|
|
981
|
-
setError("Too many attempts. Try again later.");
|
|
982
|
-
} else if (res.status === 401) {
|
|
983
|
-
setError("Wrong PIN. Try again.");
|
|
984
|
-
} else if (res.status === 400) {
|
|
985
|
-
setError("Invalid PIN format.");
|
|
986
|
-
} else {
|
|
987
|
-
setError(body.error ?? `Error (${res.status})`);
|
|
988
|
-
}
|
|
989
|
-
setDigits("");
|
|
990
|
-
} catch (err) {
|
|
991
|
-
setError(err instanceof Error ? err.message : String(err));
|
|
992
|
-
setDigits("");
|
|
993
|
-
} finally{
|
|
994
|
-
submittingRef.current = false;
|
|
995
|
-
setSubmitting(false);
|
|
996
|
-
}
|
|
997
|
-
}, []);
|
|
998
|
-
const append = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)((d)=>{
|
|
999
|
-
if (submitting || retryAfterSec > 0) return;
|
|
1000
|
-
setError(null);
|
|
1001
|
-
setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
|
|
1002
|
-
}, [
|
|
1003
|
-
submitting,
|
|
1004
|
-
retryAfterSec
|
|
1005
|
-
]);
|
|
1006
|
-
// Auto-submit once the buffer hits 6 digits. Effect runs once per
|
|
1007
|
-
// state transition (not per updater invocation), so we POST exactly
|
|
1008
|
-
// one time even under StrictMode double-render.
|
|
1009
|
-
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1010
|
-
if (digits.length === PIN_LENGTH && !submittingRef.current) {
|
|
1011
|
-
void submit(digits);
|
|
1012
|
-
}
|
|
1013
|
-
}, [
|
|
1014
|
-
digits,
|
|
1015
|
-
submit
|
|
1016
|
-
]);
|
|
1017
|
-
const backspace = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{
|
|
1018
|
-
if (submitting) return;
|
|
1019
|
-
setError(null);
|
|
1020
|
-
setDigits((cur)=>cur.slice(0, -1));
|
|
1021
|
-
}, [
|
|
1022
|
-
submitting
|
|
1023
|
-
]);
|
|
1024
|
-
// Physical keyboard support.
|
|
1025
|
-
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1026
|
-
function onKey(e) {
|
|
1027
|
-
if (/^[0-9]$/.test(e.key)) {
|
|
1028
|
-
e.preventDefault();
|
|
1029
|
-
append(e.key);
|
|
1030
|
-
} else if (e.key === "Backspace") {
|
|
1031
|
-
e.preventDefault();
|
|
1032
|
-
backspace();
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
window.addEventListener("keydown", onKey);
|
|
1036
|
-
return ()=>window.removeEventListener("keydown", onKey);
|
|
1037
|
-
}, [
|
|
1038
|
-
append,
|
|
1039
|
-
backspace
|
|
1040
|
-
]);
|
|
1041
|
-
// Tick down the rate-limit countdown.
|
|
1042
|
-
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1043
|
-
if (retryAfterSec <= 0) return;
|
|
1044
|
-
const t = setInterval(()=>{
|
|
1045
|
-
setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
|
|
1046
|
-
}, 1000);
|
|
1047
|
-
return ()=>clearInterval(t);
|
|
936
|
+
|
|
937
|
+
function UnlockScreen({ onUnlock } = {}) {
|
|
938
|
+
const router = (0,next_navigation__WEBPACK_IMPORTED_MODULE_1__.useRouter)();
|
|
939
|
+
const onSuccess = (0,react__WEBPACK_IMPORTED_MODULE_2__.useCallback)(()=>{
|
|
940
|
+
if (onUnlock) onUnlock();
|
|
941
|
+
else router.refresh();
|
|
1048
942
|
}, [
|
|
1049
|
-
|
|
943
|
+
onUnlock,
|
|
944
|
+
router
|
|
1050
945
|
]);
|
|
1051
|
-
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
style: {
|
|
1055
|
-
paddingTop: "env(safe-area-inset-top)",
|
|
1056
|
-
paddingBottom: "env(safe-area-inset-bottom)"
|
|
1057
|
-
},
|
|
1058
|
-
children: [
|
|
1059
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__/* .Logo */ .g, {
|
|
1060
|
-
className: "h-16 w-auto"
|
|
1061
|
-
}),
|
|
1062
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1063
|
-
className: "w-full max-w-xs p-6",
|
|
1064
|
-
children: [
|
|
1065
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
|
|
1066
|
-
className: "mb-1 text-center text-lg font-semibold text-fg",
|
|
1067
|
-
children: "Unlock Jarela"
|
|
1068
|
-
}),
|
|
1069
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1070
|
-
className: "mb-6 text-center text-xs text-fg-faint",
|
|
1071
|
-
children: "Enter your 6-digit PIN to decrypt your data."
|
|
1072
|
-
}),
|
|
1073
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
|
|
1074
|
-
className: "mb-6 flex justify-center gap-3",
|
|
1075
|
-
"aria-label": "PIN entry progress",
|
|
1076
|
-
children: Array.from({
|
|
1077
|
-
length: PIN_LENGTH
|
|
1078
|
-
}).map((_, i)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
|
|
1079
|
-
className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
|
|
1080
|
-
}, i))
|
|
1081
|
-
}),
|
|
1082
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1083
|
-
className: "grid grid-cols-3 gap-2",
|
|
1084
|
-
children: [
|
|
1085
|
-
[
|
|
1086
|
-
"1",
|
|
1087
|
-
"2",
|
|
1088
|
-
"3",
|
|
1089
|
-
"4",
|
|
1090
|
-
"5",
|
|
1091
|
-
"6",
|
|
1092
|
-
"7",
|
|
1093
|
-
"8",
|
|
1094
|
-
"9"
|
|
1095
|
-
].map((d)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1096
|
-
digit: d,
|
|
1097
|
-
onPress: ()=>append(d),
|
|
1098
|
-
disabled: submitting || retryAfterSec > 0
|
|
1099
|
-
}, d)),
|
|
1100
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {}),
|
|
1101
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1102
|
-
digit: "0",
|
|
1103
|
-
onPress: ()=>append("0"),
|
|
1104
|
-
disabled: submitting || retryAfterSec > 0
|
|
1105
|
-
}),
|
|
1106
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1107
|
-
digit: "←",
|
|
1108
|
-
onPress: backspace,
|
|
1109
|
-
disabled: submitting || digits.length === 0,
|
|
1110
|
-
ariaLabel: "Backspace"
|
|
1111
|
-
})
|
|
1112
|
-
]
|
|
1113
|
-
}),
|
|
1114
|
-
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1115
|
-
className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
|
|
1116
|
-
role: "status",
|
|
1117
|
-
"aria-live": "polite",
|
|
1118
|
-
children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? "Unlocking…" : "\u00A0")
|
|
1119
|
-
})
|
|
1120
|
-
]
|
|
1121
|
-
})
|
|
1122
|
-
]
|
|
1123
|
-
});
|
|
1124
|
-
}
|
|
1125
|
-
function PinKey({ digit, onPress, disabled, ariaLabel }) {
|
|
1126
|
-
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
|
|
1127
|
-
type: "button",
|
|
1128
|
-
onClick: onPress,
|
|
1129
|
-
disabled: disabled,
|
|
1130
|
-
"aria-label": ariaLabel ?? digit,
|
|
1131
|
-
className: "h-14 rounded-xl bg-surface-3 text-xl font-medium text-fg transition-colors hover:bg-surface-3/70 active:bg-surface-3/50 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1132
|
-
children: digit
|
|
946
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_PinKeypad__WEBPACK_IMPORTED_MODULE_3__/* .PinKeypad */ .y, {
|
|
947
|
+
mode: "decrypt",
|
|
948
|
+
onSuccess: onSuccess
|
|
1133
949
|
});
|
|
1134
950
|
}
|
|
1135
951
|
|
|
@@ -1757,6 +1573,241 @@ function() { throw new Error("Attempted to call UnlockScreen() from the server b
|
|
|
1757
1573
|
"UnlockScreen",
|
|
1758
1574
|
);
|
|
1759
1575
|
|
|
1576
|
+
/***/ }),
|
|
1577
|
+
|
|
1578
|
+
/***/ 71654:
|
|
1579
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
1580
|
+
|
|
1581
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
1582
|
+
/* harmony export */ y: () => (/* binding */ PinKeypad)
|
|
1583
|
+
/* harmony export */ });
|
|
1584
|
+
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(48249);
|
|
1585
|
+
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__);
|
|
1586
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(67484);
|
|
1587
|
+
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
|
|
1588
|
+
/* harmony import */ var _components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(41357);
|
|
1589
|
+
/* __next_internal_client_entry_do_not_use__ PinKeypad auto */
|
|
1590
|
+
|
|
1591
|
+
|
|
1592
|
+
// Shared 6-digit PIN keypad used by both the decrypt splash (master key
|
|
1593
|
+
// locked at boot) and the screen-lock overlay (idle timer fired). The
|
|
1594
|
+
// only differences between the two states are (a) the endpoint hit
|
|
1595
|
+
// (b) the copy on screen and (c) what happens after success. Everything
|
|
1596
|
+
// else — keypad layout, dot strip, rate-limit handling, keyboard
|
|
1597
|
+
// support, error mapping — is identical, so the two were collapsed into
|
|
1598
|
+
// one component to avoid drift.
|
|
1599
|
+
const PIN_LENGTH = 6;
|
|
1600
|
+
const MODES = {
|
|
1601
|
+
decrypt: {
|
|
1602
|
+
endpoint: "/api/v1/security/unlock",
|
|
1603
|
+
title: "Decrypt Jarela",
|
|
1604
|
+
subtitle: "Enter your 6-digit PIN to decrypt your data.",
|
|
1605
|
+
busyLabel: "Decrypting\u2026"
|
|
1606
|
+
},
|
|
1607
|
+
unlock: {
|
|
1608
|
+
endpoint: "/api/v1/security/verify-pin",
|
|
1609
|
+
title: "Welcome back",
|
|
1610
|
+
subtitle: "Enter your 6-digit PIN to unlock.",
|
|
1611
|
+
busyLabel: "Unlocking\u2026"
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
function PinKeypad({ mode, onSuccess }) {
|
|
1615
|
+
const cfg = MODES[mode];
|
|
1616
|
+
const [digits, setDigits] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
|
|
1617
|
+
const [error, setError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
|
|
1618
|
+
const [submitting, setSubmitting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
|
|
1619
|
+
const [retryAfterSec, setRetryAfterSec] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(0);
|
|
1620
|
+
// Hard guard against parallel submits. setState updaters can run more
|
|
1621
|
+
// than once (dev StrictMode, concurrent rendering), so if `submit`
|
|
1622
|
+
// were called from inside `setDigits` we'd POST twice and the second
|
|
1623
|
+
// request would race the first into `unlockMasterKey()` after state
|
|
1624
|
+
// already flipped to unlocked — the route would 500.
|
|
1625
|
+
const submittingRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(false);
|
|
1626
|
+
const submit = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(async (pin)=>{
|
|
1627
|
+
if (submittingRef.current) return;
|
|
1628
|
+
submittingRef.current = true;
|
|
1629
|
+
setSubmitting(true);
|
|
1630
|
+
setError(null);
|
|
1631
|
+
try {
|
|
1632
|
+
const res = await fetch(cfg.endpoint, {
|
|
1633
|
+
method: "POST",
|
|
1634
|
+
headers: {
|
|
1635
|
+
"content-type": "application/json"
|
|
1636
|
+
},
|
|
1637
|
+
body: JSON.stringify({
|
|
1638
|
+
pin
|
|
1639
|
+
})
|
|
1640
|
+
});
|
|
1641
|
+
if (res.ok) {
|
|
1642
|
+
onSuccess();
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
const body = await res.json().catch(()=>({}));
|
|
1646
|
+
// Goal-state convergence: both endpoints can return 409 when the
|
|
1647
|
+
// server already reached the target state (decrypt → master key
|
|
1648
|
+
// already unlocked; unlock → screen not locked). Treat as success
|
|
1649
|
+
// rather than surfacing a confusing error.
|
|
1650
|
+
if (res.status === 409 && (body.error === "not-locked" || body.error === "no-pin")) {
|
|
1651
|
+
onSuccess();
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
if (res.status === 429 && typeof body.retry_after_ms === "number") {
|
|
1655
|
+
setRetryAfterSec(Math.ceil(body.retry_after_ms / 1000));
|
|
1656
|
+
setError("Too many attempts. Try again later.");
|
|
1657
|
+
} else if (res.status === 401) {
|
|
1658
|
+
setError("Wrong PIN. Try again.");
|
|
1659
|
+
} else if (res.status === 400) {
|
|
1660
|
+
setError("Invalid PIN format.");
|
|
1661
|
+
} else {
|
|
1662
|
+
setError(body.error ?? `Error (${res.status})`);
|
|
1663
|
+
}
|
|
1664
|
+
setDigits("");
|
|
1665
|
+
} catch (err) {
|
|
1666
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
1667
|
+
setDigits("");
|
|
1668
|
+
} finally{
|
|
1669
|
+
submittingRef.current = false;
|
|
1670
|
+
setSubmitting(false);
|
|
1671
|
+
}
|
|
1672
|
+
}, [
|
|
1673
|
+
cfg.endpoint,
|
|
1674
|
+
onSuccess
|
|
1675
|
+
]);
|
|
1676
|
+
const append = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)((d)=>{
|
|
1677
|
+
if (submitting || retryAfterSec > 0) return;
|
|
1678
|
+
setError(null);
|
|
1679
|
+
setDigits((cur)=>cur.length >= PIN_LENGTH ? cur : cur + d);
|
|
1680
|
+
}, [
|
|
1681
|
+
submitting,
|
|
1682
|
+
retryAfterSec
|
|
1683
|
+
]);
|
|
1684
|
+
// Auto-submit once the buffer hits 6 digits.
|
|
1685
|
+
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1686
|
+
if (digits.length === PIN_LENGTH && !submittingRef.current) {
|
|
1687
|
+
void submit(digits);
|
|
1688
|
+
}
|
|
1689
|
+
}, [
|
|
1690
|
+
digits,
|
|
1691
|
+
submit
|
|
1692
|
+
]);
|
|
1693
|
+
const backspace = (0,react__WEBPACK_IMPORTED_MODULE_1__.useCallback)(()=>{
|
|
1694
|
+
if (submitting) return;
|
|
1695
|
+
setError(null);
|
|
1696
|
+
setDigits((cur)=>cur.slice(0, -1));
|
|
1697
|
+
}, [
|
|
1698
|
+
submitting
|
|
1699
|
+
]);
|
|
1700
|
+
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1701
|
+
function onKey(e) {
|
|
1702
|
+
if (/^[0-9]$/.test(e.key)) {
|
|
1703
|
+
e.preventDefault();
|
|
1704
|
+
append(e.key);
|
|
1705
|
+
} else if (e.key === "Backspace") {
|
|
1706
|
+
e.preventDefault();
|
|
1707
|
+
backspace();
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
window.addEventListener("keydown", onKey);
|
|
1711
|
+
return ()=>window.removeEventListener("keydown", onKey);
|
|
1712
|
+
}, [
|
|
1713
|
+
append,
|
|
1714
|
+
backspace
|
|
1715
|
+
]);
|
|
1716
|
+
process.env.__NEXT_PRIVATE_MINIMIZE_MACRO_FALSE && (0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
|
|
1717
|
+
if (retryAfterSec <= 0) return;
|
|
1718
|
+
const t = setInterval(()=>{
|
|
1719
|
+
setRetryAfterSec((s)=>s > 0 ? s - 1 : 0);
|
|
1720
|
+
}, 1000);
|
|
1721
|
+
return ()=>clearInterval(t);
|
|
1722
|
+
}, [
|
|
1723
|
+
retryAfterSec
|
|
1724
|
+
]);
|
|
1725
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1726
|
+
className: "fixed inset-0 z-[1000] flex flex-col items-center justify-center gap-6 bg-surface text-fg animate-in fade-in duration-200",
|
|
1727
|
+
style: {
|
|
1728
|
+
paddingTop: "env(safe-area-inset-top)",
|
|
1729
|
+
paddingBottom: "env(safe-area-inset-bottom)"
|
|
1730
|
+
},
|
|
1731
|
+
"data-pin-mode": mode,
|
|
1732
|
+
children: [
|
|
1733
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_ui_Logo__WEBPACK_IMPORTED_MODULE_2__/* .Logo */ .g, {
|
|
1734
|
+
className: "h-16 w-auto"
|
|
1735
|
+
}),
|
|
1736
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1737
|
+
className: "w-full max-w-xs p-6",
|
|
1738
|
+
children: [
|
|
1739
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
|
|
1740
|
+
className: "mb-1 text-center text-lg font-semibold text-fg",
|
|
1741
|
+
children: cfg.title
|
|
1742
|
+
}),
|
|
1743
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1744
|
+
className: "mb-6 text-center text-xs text-fg-faint",
|
|
1745
|
+
children: cfg.subtitle
|
|
1746
|
+
}),
|
|
1747
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
|
|
1748
|
+
className: "mb-6 flex justify-center gap-3",
|
|
1749
|
+
"aria-label": "PIN entry progress",
|
|
1750
|
+
children: Array.from({
|
|
1751
|
+
length: PIN_LENGTH
|
|
1752
|
+
}).map((_, i)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
|
|
1753
|
+
className: `h-3 w-3 rounded-full transition-colors ${i < digits.length ? error ? "bg-red-500" : "bg-fg" : "bg-surface-3"}`
|
|
1754
|
+
}, i))
|
|
1755
|
+
}),
|
|
1756
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
|
|
1757
|
+
className: "grid grid-cols-3 gap-2",
|
|
1758
|
+
children: [
|
|
1759
|
+
[
|
|
1760
|
+
"1",
|
|
1761
|
+
"2",
|
|
1762
|
+
"3",
|
|
1763
|
+
"4",
|
|
1764
|
+
"5",
|
|
1765
|
+
"6",
|
|
1766
|
+
"7",
|
|
1767
|
+
"8",
|
|
1768
|
+
"9"
|
|
1769
|
+
].map((d)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1770
|
+
digit: d,
|
|
1771
|
+
onPress: ()=>append(d),
|
|
1772
|
+
disabled: submitting || retryAfterSec > 0
|
|
1773
|
+
}, d)),
|
|
1774
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {}),
|
|
1775
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1776
|
+
digit: "0",
|
|
1777
|
+
onPress: ()=>append("0"),
|
|
1778
|
+
disabled: submitting || retryAfterSec > 0
|
|
1779
|
+
}),
|
|
1780
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(PinKey, {
|
|
1781
|
+
digit: "\\u2190",
|
|
1782
|
+
onPress: backspace,
|
|
1783
|
+
disabled: submitting || digits.length === 0,
|
|
1784
|
+
ariaLabel: "Backspace"
|
|
1785
|
+
})
|
|
1786
|
+
]
|
|
1787
|
+
}),
|
|
1788
|
+
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
|
|
1789
|
+
className: `mt-4 min-h-[1.5rem] text-center text-xs ${error ? "text-red-400" : "text-fg-faint"}`,
|
|
1790
|
+
role: "status",
|
|
1791
|
+
"aria-live": "polite",
|
|
1792
|
+
children: retryAfterSec > 0 ? `Try again in ${retryAfterSec}s` : error ?? (submitting ? cfg.busyLabel : "\u00A0")
|
|
1793
|
+
})
|
|
1794
|
+
]
|
|
1795
|
+
})
|
|
1796
|
+
]
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
function PinKey({ digit, onPress, disabled, ariaLabel }) {
|
|
1800
|
+
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
|
|
1801
|
+
type: "button",
|
|
1802
|
+
onClick: onPress,
|
|
1803
|
+
disabled: disabled,
|
|
1804
|
+
"aria-label": ariaLabel ?? digit,
|
|
1805
|
+
className: "h-14 rounded-xl bg-surface-3 text-xl font-medium text-fg transition-colors hover:bg-surface-3/70 active:bg-surface-3/50 disabled:cursor-not-allowed disabled:opacity-50",
|
|
1806
|
+
children: digit
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
|
|
1760
1811
|
/***/ }),
|
|
1761
1812
|
|
|
1762
1813
|
/***/ 95984:
|