@hirra/vibemeter 0.1.2 → 0.1.4
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/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +7 -1
- package/.next/build-manifest.json +7 -8
- package/.next/fallback-build-manifest.json +3 -3
- package/.next/prerender-manifest.json +16 -12
- package/.next/required-server-files.js +5 -4
- package/.next/required-server-files.json +5 -4
- package/.next/routes-manifest.json +36 -0
- package/.next/server/app/_global-error/page/build-manifest.json +4 -5
- package/.next/server/app/_global-error/page.js +4 -4
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page/build-manifest.json +4 -5
- package/.next/server/app/_not-found/page.js +5 -4
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/admin/page/build-manifest.json +4 -5
- package/.next/server/app/admin/page.js +7 -5
- package/.next/server/app/admin/page.js.nft.json +1 -1
- package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/codex-accounts/route.js +1 -1
- package/.next/server/app/api/codex-accounts/route.js.nft.json +1 -1
- package/.next/server/app/api/float/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/float/route/build-manifest.json +9 -0
- package/.next/server/app/api/float/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/float/route.js +8 -0
- package/.next/server/app/api/float/route.js.map +5 -0
- package/.next/server/app/api/float/route.js.nft.json +1 -0
- package/.next/server/app/api/float/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/import-sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/tags/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/tags/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/alerts/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/settings/alerts/route/build-manifest.json +9 -0
- package/.next/server/app/api/settings/alerts/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/settings/alerts/route.js +9 -0
- package/.next/server/app/api/settings/alerts/route.js.map +5 -0
- package/.next/server/app/api/settings/alerts/route.js.nft.json +1 -0
- package/.next/server/app/api/settings/alerts/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/settings/notify/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/settings/notify/route/build-manifest.json +9 -0
- package/.next/server/app/api/settings/notify/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/settings/notify/route.js +7 -0
- package/.next/server/app/api/settings/notify/route.js.map +5 -0
- package/.next/server/app/api/settings/notify/route.js.nft.json +1 -0
- package/.next/server/app/api/settings/notify/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/usage/route.js +1 -1
- package/.next/server/app/api/usage/route.js.nft.json +1 -1
- package/.next/server/app/float/page/app-paths-manifest.json +3 -0
- package/.next/server/app/float/page/build-manifest.json +16 -0
- package/.next/server/app/float/page/next-font-manifest.json +11 -0
- package/.next/server/app/float/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/float/page/server-reference-manifest.json +4 -0
- package/.next/server/app/float/page.js +15 -0
- package/.next/server/app/float/page.js.map +5 -0
- package/.next/server/app/float/page.js.nft.json +1 -0
- package/.next/server/app/float/page_client-reference-manifest.js +3 -0
- package/.next/server/app/install.sh/route/app-paths-manifest.json +3 -0
- package/.next/server/app/install.sh/route/build-manifest.json +9 -0
- package/.next/server/app/install.sh/route/server-reference-manifest.json +4 -0
- package/.next/server/app/install.sh/route.js +6 -0
- package/.next/server/app/install.sh/route.js.map +5 -0
- package/.next/server/app/install.sh/route.js.nft.json +1 -0
- package/.next/server/app/install.sh/route_client-reference-manifest.js +3 -0
- package/.next/server/app/install.sh.body +108 -0
- package/.next/server/app/install.sh.meta +1 -0
- package/.next/server/app/page/build-manifest.json +4 -5
- package/.next/server/app/page.js +8 -5
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/settings/page/app-paths-manifest.json +3 -0
- package/.next/server/app/settings/page/build-manifest.json +16 -0
- package/.next/server/app/settings/page/next-font-manifest.json +11 -0
- package/.next/server/app/settings/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/settings/page/server-reference-manifest.json +4 -0
- package/.next/server/app/settings/page.js +17 -0
- package/.next/server/app/settings/page.js.map +5 -0
- package/.next/server/app/settings/page.js.nft.json +1 -0
- package/.next/server/app/settings/page_client-reference-manifest.js +3 -0
- package/.next/server/app-paths-manifest.json +7 -1
- package/.next/server/chunks/[externals]__0_i~3ox._.js +3 -0
- package/.next/server/chunks/[externals]__0_i~3ox._.js.map +1 -0
- package/.next/server/chunks/[externals]__13p_1zh._.js +3 -0
- package/.next/server/chunks/[externals]__13p_1zh._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__01t2c3w._.js +38 -0
- package/.next/server/chunks/[root-of-the-server]__01t2c3w._.js.map +1 -0
- package/.next/server/chunks/{[root-of-the-server]__0g0u0lm._.js → [root-of-the-server]__024yzee._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__00q0o~z._.js → [root-of-the-server]__082iwfa._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__0chedn~._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0mwf0bn._.js +38 -0
- package/.next/server/chunks/[root-of-the-server]__0mwf0bn._.js.map +1 -0
- package/.next/server/chunks/{[root-of-the-server]__0-74syk._.js → [root-of-the-server]__0ru3_it._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__0u6y_k1._.js +111 -0
- package/.next/server/chunks/[root-of-the-server]__0u6y_k1._.js.map +1 -0
- package/.next/server/chunks/{[root-of-the-server]__0866q87._.js → [root-of-the-server]__0xa4dzi._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__13415c6._.js +96 -0
- package/.next/server/chunks/[root-of-the-server]__13415c6._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__13j_28o._.js +96 -0
- package/.next/server/chunks/[root-of-the-server]__13j_28o._.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_float_route_actions_012j~jr.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_float_route_actions_012j~jr.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_settings_alerts_route_actions_0ydcyko.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_settings_alerts_route_actions_0ydcyko.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_settings_notify_route_actions_0-j35mb.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_settings_notify_route_actions_0-j35mb.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_install_sh_route_actions_0cj-6me.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_install_sh_route_actions_0cj-6me.js.map +1 -0
- package/.next/server/chunks/node_modules_next_06f88ko._.js +19 -0
- package/.next/server/chunks/node_modules_next_06f88ko._.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_03jj5jj.js +18 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_03jj5jj.js.map +1 -0
- package/.next/server/chunks/src_12i1qk5._.js +3 -0
- package/.next/server/chunks/src_12i1qk5._.js.map +1 -0
- package/.next/server/chunks/src_lib_alerts_ticker_ts_0n6oy1d._.js +169 -0
- package/.next/server/chunks/src_lib_alerts_ticker_ts_0n6oy1d._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03mt8o0._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__03mt8o0._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__098zro9._.js → [root-of-the-server]__090jxzh._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__090jxzh._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__09hgk-7._.js → [root-of-the-server]__0c3j40u._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0c3j40u._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ot4nev._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0ot4nev._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0q_~l2m._.js +90 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0q_~l2m._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__0cqes87._.js → [root-of-the-server]__0qt26ty._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0qt26ty._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0s5.uhg._.js +55 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0s5.uhg._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0x~-phx._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0x~-phx._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0y_zq8k._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0y_zq8k._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__08qr2ji._.js → [root-of-the-server]__0~953ob._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__0~953ob._.js.map +1 -0
- package/.next/server/chunks/ssr/_0-9j34y._.js +70 -0
- package/.next/server/chunks/ssr/_0-9j34y._.js.map +1 -0
- package/.next/server/chunks/ssr/_0jxmm9h._.js +6 -0
- package/.next/server/chunks/ssr/_0jxmm9h._.js.map +1 -0
- package/.next/server/chunks/ssr/_0lfe3wr._.js +3 -0
- package/.next/server/chunks/ssr/_0lfe3wr._.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_0i2xw~e._.js → _0nvprxh._.js} +5 -2
- package/.next/server/chunks/ssr/_0nvprxh._.js.map +1 -0
- package/.next/server/chunks/ssr/_0pugb10._.js +6 -0
- package/.next/server/chunks/ssr/_0pugb10._.js.map +1 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_float_page_actions_0x6hm4p.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_float_page_actions_0x6hm4p.js.map +1 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_settings_page_actions_0mr68ai.js +3 -0
- package/.next/server/chunks/ssr/_next-internal_server_app_settings_page_actions_0mr68ai.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_@swc_helpers_cjs__interop_require_default_cjs_11~q6fv._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_@swc_helpers_cjs__interop_require_default_cjs_11~q6fv._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_09jzzl8._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_09jzzl8._.js.map +1 -0
- package/.next/server/chunks/ssr/{node_modules_next_dist_0e1izl_._.js → node_modules_next_dist_0h9llsw._.js} +2 -2
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_06b_a87.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_06b_a87.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0p_u4px.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0p_u4px.js.map +1 -0
- package/.next/server/chunks/ssr/src_0urkups._.js +3 -0
- package/.next/server/chunks/ssr/src_0urkups._.js.map +1 -0
- package/.next/server/chunks/ssr/src_components_0ox9d~w._.js +3 -0
- package/.next/server/chunks/ssr/src_components_0ox9d~w._.js.map +1 -0
- package/.next/server/chunks/ssr/src_components_CodexAccountsPanel_tsx_0n9xjzi._.js +1 -1
- package/.next/server/chunks/ssr/src_components_CodexAccountsPanel_tsx_0n9xjzi._.js.map +1 -1
- package/.next/server/chunks/ssr/src_components_FloatingWidget_tsx_089.6oo._.js +3 -0
- package/.next/server/chunks/ssr/src_components_FloatingWidget_tsx_089.6oo._.js.map +1 -0
- package/.next/server/chunks/ssr/src_lib_i18n_client_tsx_07pysgz._.js +3 -0
- package/.next/server/chunks/ssr/src_lib_i18n_client_tsx_07pysgz._.js.map +1 -0
- package/.next/server/edge/chunks/_0-m5tdn._.js +3 -0
- package/.next/server/edge/chunks/_0-m5tdn._.js.map +1 -0
- package/.next/server/edge/chunks/node_modules_next_dist_esm_build_templates_edge-wrapper_0mz-5sp.js.map +1 -0
- package/.next/server/edge/chunks/turbopack-node_modules_next_dist_esm_build_templates_edge-wrapper_0mz-5sp.js +3 -0
- package/.next/server/functions-config-manifest.json +4 -1
- package/.next/server/instrumentation/middleware-manifest.json +12 -0
- package/.next/server/instrumentation.js +4 -0
- package/.next/server/instrumentation.js.map +5 -0
- package/.next/server/instrumentation.js.nft.json +1 -0
- package/.next/server/middleware-build-manifest.js +7 -8
- package/.next/server/next-font-manifest.js +1 -1
- package/.next/server/next-font-manifest.json +8 -0
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +0 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/0bymg5d-lqs0o.js +5 -0
- package/.next/static/chunks/0ebdqsutnp8y4.js +1 -0
- package/.next/static/chunks/0i3~mnai-l497.js +1 -0
- package/.next/static/chunks/0idj4tdmyr934.js +1 -0
- package/.next/static/chunks/10eoi4q2wk80z.js +1 -0
- package/.next/static/chunks/10refkmyp9rbl.css +3 -0
- package/.next/static/chunks/{07lhk_q6pmm3r.js → 10u3y4bw1ayzs.js} +1 -1
- package/.next/static/chunks/{00gq-v0e07i1q.js → 115dplafwys-z.js} +1 -1
- package/.next/static/chunks/12-9memveha-v.js +1 -0
- package/.next/static/chunks/163s8y.j70104.js +4 -0
- package/.next/static/chunks/turbopack-0k9twle9a8sh6.js +1 -0
- package/.next/types/routes.d.ts +8 -2
- package/.next/types/validator.ts +54 -0
- package/README.md +57 -17
- package/bin/vibemeter-float.swift +970 -0
- package/bin/vibemeter-notify.sh +172 -0
- package/bin/vibemeter.mjs +385 -7
- package/package.json +2 -1
- package/public/demo1.png +0 -0
- package/public/float-ball.png +0 -0
- package/public/float-collapsed.png +0 -0
- package/public/float-expanded.png +0 -0
- package/public/pay-alipay.jpg +0 -0
- package/.next/server/app/_not-found.html +0 -1
- package/.next/server/app/_not-found.meta +0 -16
- package/.next/server/app/_not-found.rsc +0 -16
- package/.next/server/app/_not-found.segments/_full.segment.rsc +0 -16
- package/.next/server/app/_not-found.segments/_head.segment.rsc +0 -6
- package/.next/server/app/_not-found.segments/_index.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -5
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
- package/.next/server/chunks/[root-of-the-server]__0y68a5d._.js +0 -96
- package/.next/server/chunks/[root-of-the-server]__0y68a5d._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__054lv8d._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__054lv8d._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__08qr2ji._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__098zro9._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__09hgk-7._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0cqes87._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0l.drdn._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0l.drdn._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0v1zqf~._.js +0 -120
- package/.next/server/chunks/ssr/[root-of-the-server]__0v1zqf~._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js +0 -3
- package/.next/server/chunks/ssr/[root-of-the-server]__0v73tbn._.js.map +0 -1
- package/.next/server/chunks/ssr/_0ye~8el._.js +0 -7
- package/.next/server/chunks/ssr/_0ye~8el._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_0ck2~9g._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_0i2xw~e._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_0e1izl_._.js.map +0 -1
- package/.next/server/chunks/ssr/node_modules_next_dist_0gsjr7_._.js +0 -3
- package/.next/server/chunks/ssr/node_modules_next_dist_0gsjr7_._.js.map +0 -1
- package/.next/server/pages/404.html +0 -1
- package/.next/static/chunks/002~6040mndie.css +0 -3
- package/.next/static/chunks/02g3221oh~3le.js +0 -2
- package/.next/static/chunks/0erq0bmub6w_z.js +0 -2
- package/.next/static/chunks/0ljfidstam_7k.js +0 -1
- package/.next/static/chunks/0pqt~8bl3ukh4.js +0 -4
- package/.next/static/chunks/turbopack-14vio.b1b9i4l.js +0 -1
- /package/.next/server/chunks/{[root-of-the-server]__0g0u0lm._.js.map → [root-of-the-server]__024yzee._.js.map} +0 -0
- /package/.next/server/chunks/{[root-of-the-server]__00q0o~z._.js.map → [root-of-the-server]__082iwfa._.js.map} +0 -0
- /package/.next/server/chunks/{[root-of-the-server]__0-74syk._.js.map → [root-of-the-server]__0ru3_it._.js.map} +0 -0
- /package/.next/server/chunks/{[root-of-the-server]__0866q87._.js.map → [root-of-the-server]__0xa4dzi._.js.map} +0 -0
- /package/.next/static/{Ong62ufsHaRU36s0QK5ZZ → _Y03MiN6NI16Ms86q6vCJ}/_buildManifest.js +0 -0
- /package/.next/static/{Ong62ufsHaRU36s0QK5ZZ → _Y03MiN6NI16Ms86q6vCJ}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{Ong62ufsHaRU36s0QK5ZZ → _Y03MiN6NI16Ms86q6vCJ}/_ssgManifest.js +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/SettingsNotifyPanel.tsx","../../../../src/components/SettingsAlertsPanel.tsx","../../../../src/components/SettingsDonatePanel.tsx"],"sourcesContent":["'use client';\n\nimport { useState } from 'react';\nimport { useT } from '@/lib/i18n/client';\n\ntype NotifyStatus = {\n scriptPath: string;\n scriptExists: boolean;\n claudeSettingsPath: string;\n codexConfigPath: string;\n claudeStop: boolean;\n claudeNotification: boolean;\n codex: boolean;\n codexForeign: string | null;\n codexConfigExists: boolean;\n};\n\ninterface Props {\n initialStatus: NotifyStatus;\n}\n\nexport function SettingsNotifyPanel({ initialStatus }: Props) {\n const t = useT();\n const [status, setStatus] = useState<NotifyStatus>(initialStatus);\n const [includeStop, setIncludeStop] = useState(true);\n const [includeNotification, setIncludeNotification] = useState(initialStatus.claudeNotification);\n const [includeCodex, setIncludeCodex] = useState(true);\n const [pending, setPending] = useState<string | null>(null);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const enabled = status.claudeStop || status.claudeNotification || status.codex;\n\n async function run(action: 'install' | 'uninstall') {\n setPending(action);\n setMessage(null);\n setError(null);\n try {\n const res = await fetch('/api/settings/notify', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n action,\n stop: includeStop,\n notification: includeNotification,\n codex: includeCodex,\n }),\n });\n const payload = await res.json();\n if (!res.ok) throw new Error(payload.error ?? 'Operation failed');\n setStatus(payload.status);\n if (action === 'install') {\n const r = payload.result;\n const notes: string[] = [];\n if (r.codexSkipped === 'no-config') notes.push(t('notify.skipNoCodex'));\n if (r.codexSkipped === 'foreign-notify') notes.push(t('notify.skipForeign', { cmd: r.codexForeign }));\n setMessage(notes.length ? t('notify.updated', { notes: notes.join('. ') }) : t('notify.enabledMsg'));\n } else {\n setMessage(t('notify.disabledMsg'));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Operation failed');\n } finally {\n setPending(null);\n }\n }\n\n return (\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-6\">\n <div className=\"flex items-start justify-between gap-4 mb-4\">\n <div>\n <h2 className=\"text-base font-semibold text-zinc-100\">{t('notify.title')}</h2>\n <p className=\"text-zinc-500 text-xs mt-1\">\n {t('notify.subtitle', { tool: 'Claude / Codex', project: '{project}' })}\n </p>\n </div>\n <span\n className={`shrink-0 rounded-full px-2.5 py-0.5 text-[10px] uppercase tracking-wider ${\n enabled ? 'bg-emerald-900/40 text-emerald-300 border border-emerald-800' : 'bg-zinc-800 text-zinc-500 border border-zinc-700'\n }`}\n >\n {enabled ? t('notify.on') : t('notify.off')}\n </span>\n </div>\n\n <div className=\"grid sm:grid-cols-3 gap-3 mb-5\">\n <StatusPill label={t('notify.claudeStop')} on={status.claudeStop} />\n <StatusPill label={t('notify.claudeNotification')} on={status.claudeNotification} />\n <StatusPill\n label={t('notify.codexComplete')}\n on={status.codex}\n warn={!!status.codexForeign}\n warnText={status.codexForeign ? t('notify.foreignWarn') : undefined}\n />\n </div>\n\n <div className=\"space-y-2 mb-5 text-xs text-zinc-300\">\n <Toggle label={t('notify.hookStop')} checked={includeStop} onChange={setIncludeStop} />\n <Toggle label={t('notify.hookNotification')} checked={includeNotification} onChange={setIncludeNotification} />\n <Toggle\n label={t('notify.hookCodex', { state: status.codexConfigExists ? t('notify.hookCodexConfigFound') : t('notify.hookCodexConfigMissing') })}\n checked={includeCodex}\n onChange={setIncludeCodex}\n disabled={!status.codexConfigExists}\n />\n </div>\n\n {status.codexForeign && (\n <div className=\"mb-4 rounded border border-amber-900/60 bg-amber-950/30 px-3 py-2 text-xs text-amber-200\">\n {t('notify.foreignBody')}\n <pre className=\"mt-1 whitespace-pre-wrap text-amber-300/80\">{status.codexForeign}</pre>\n {t('notify.foreignRemove')}\n </div>\n )}\n\n <div className=\"flex items-center gap-3\">\n <button\n type=\"button\"\n onClick={() => run('install')}\n disabled={pending !== null}\n className=\"rounded-md bg-violet-600 px-3 py-2 text-xs text-white hover:bg-violet-500 disabled:opacity-50\"\n >\n {pending === 'install' ? t('notify.installing') : enabled ? t('notify.reapply') : t('notify.enable')}\n </button>\n {enabled && (\n <button\n type=\"button\"\n onClick={() => run('uninstall')}\n disabled={pending !== null}\n className=\"rounded-md border border-zinc-700 px-3 py-2 text-xs text-zinc-300 hover:border-zinc-500 disabled:opacity-50\"\n >\n {pending === 'uninstall' ? t('notify.removing') : t('notify.disable')}\n </button>\n )}\n </div>\n\n {!status.scriptExists && (\n <p className=\"mt-3 text-xs text-red-400\">{t('notify.scriptMissing', { path: status.scriptPath })}</p>\n )}\n {message && <p className=\"mt-3 text-xs text-emerald-400\">{message}</p>}\n {error && <p className=\"mt-3 text-xs text-red-400\">{error}</p>}\n </section>\n );\n}\n\nfunction StatusPill({ label, on, warn, warnText }: { label: string; on: boolean; warn?: boolean; warnText?: string }) {\n return (\n <div className=\"flex items-center justify-between rounded border border-zinc-800 bg-zinc-950/50 px-3 py-2 text-xs\">\n <span className=\"text-zinc-400\">{label}</span>\n {warn ? (\n <span className=\"text-amber-400\" title={warnText}>!</span>\n ) : (\n <span className={on ? 'text-emerald-400' : 'text-zinc-600'}>{on ? '✓' : '·'}</span>\n )}\n </div>\n );\n}\n\nfunction Toggle({ label, checked, onChange, disabled }: { label: string; checked: boolean; onChange: (v: boolean) => void; disabled?: boolean }) {\n return (\n <label className={`flex items-center gap-2 ${disabled ? 'opacity-50' : 'cursor-pointer'}`}>\n <input\n type=\"checkbox\"\n checked={checked}\n disabled={disabled}\n onChange={(e) => onChange(e.target.checked)}\n className=\"h-3.5 w-3.5 accent-violet-500\"\n />\n <span>{label}</span>\n </label>\n );\n}\n","'use client';\n\nimport { useCallback, useMemo, useState } from 'react';\nimport { useT } from '@/lib/i18n/client';\n\n// Mirror server types loosely so the panel can import without dragging in\n// node-only modules. Webhooks come back masked from GET and are sent back\n// as the sentinel on save if the user hasn't edited the field.\nconst WEBHOOK_UNCHANGED_SENTINEL = '__VIBEMETER_WEBHOOK_UNCHANGED__';\n\ntype ChannelType = 'wxwork' | 'generic';\ntype AlertMetric = 'claude_5h_remaining_pct' | 'claude_weekly_remaining_pct' | 'codex_5h_remaining_pct' | 'codex_weekly_remaining_pct';\ntype ResetMetric = 'claude_5h' | 'claude_weekly' | 'codex_5h' | 'codex_weekly';\n\ninterface Channel {\n id: string;\n type: ChannelType;\n label: string;\n webhook: string;\n headers?: Record<string, string>;\n}\n\ntype Rule =\n | { id: string; kind: 'threshold'; label?: string; metric: AlertMetric; below: number; channelIds: string[]; enabled: boolean }\n | { id: string; kind: 'daily'; label?: string; hour: number; minute: number; channelIds: string[]; enabled: boolean }\n | { id: string; kind: 'reset_reminder'; label?: string; metric: ResetMetric; minutesBefore: number; remainingPctAbove: number; channelIds: string[]; enabled: boolean };\n\ntype PushLocale = 'zh' | 'en';\ninterface Config { channels: Channel[]; rules: Rule[]; pushLocale?: PushLocale }\n\nconst METRIC_KEYS: AlertMetric[] = ['claude_5h_remaining_pct', 'claude_weekly_remaining_pct', 'codex_5h_remaining_pct', 'codex_weekly_remaining_pct'];\nconst RESET_METRIC_KEYS: ResetMetric[] = ['claude_5h', 'claude_weekly', 'codex_5h', 'codex_weekly'];\n\nfunction newId(): string {\n if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) return (crypto as Crypto).randomUUID();\n return `id-${Math.random().toString(36).slice(2, 10)}-${Date.now().toString(36)}`;\n}\n\nfunction newChannel(type: ChannelType, defaultLabel: string): Channel {\n return {\n id: newId(),\n type,\n label: defaultLabel,\n webhook: '',\n headers: type === 'generic' ? {} : undefined,\n };\n}\n\nfunction newRule(kind: Rule['kind'], firstChannelId: string | null): Rule {\n const channelIds = firstChannelId ? [firstChannelId] : [];\n if (kind === 'threshold') {\n return { id: newId(), kind, metric: 'claude_5h_remaining_pct', below: 20, channelIds, enabled: true };\n }\n if (kind === 'daily') {\n return { id: newId(), kind, hour: 9, minute: 0, channelIds, enabled: true };\n }\n return { id: newId(), kind: 'reset_reminder', metric: 'claude_5h', minutesBefore: 60, remainingPctAbove: 50, channelIds, enabled: true };\n}\n\ninterface Props {\n initialConfig: Config;\n initialConfigPath: string;\n}\n\nexport function SettingsAlertsPanel({ initialConfig, initialConfigPath }: Props) {\n const t = useT();\n const [config, setConfig] = useState<Config>(initialConfig);\n const [dirtyWebhooks, setDirtyWebhooks] = useState<Record<string, boolean>>({});\n const [pending, setPending] = useState<string | null>(null);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [configPath] = useState<string>(initialConfigPath);\n\n const refresh = useCallback(async () => {\n try {\n const res = await fetch('/api/settings/alerts');\n const payload = await res.json();\n if (!res.ok) throw new Error(payload.error ?? 'load failed');\n setConfig({ channels: payload.config.channels ?? [], rules: payload.config.rules ?? [] });\n setDirtyWebhooks({});\n } catch (err) {\n setError(err instanceof Error ? err.message : 'load failed');\n }\n }, []);\n\n function updateChannel(id: string, patch: Partial<Channel>) {\n setConfig((c) => ({ ...c, channels: c.channels.map((ch) => (ch.id === id ? { ...ch, ...patch } : ch)) }));\n }\n function removeChannel(id: string) {\n setConfig((c) => ({\n channels: c.channels.filter((ch) => ch.id !== id),\n rules: c.rules.map((r) => ({ ...r, channelIds: r.channelIds.filter((cid) => cid !== id) })),\n }));\n }\n function addChannel(type: ChannelType) {\n const ch = newChannel(type, type === 'wxwork' ? t('alerts.defaultLabelWxwork') : t('alerts.defaultLabelGeneric'));\n setConfig((c) => ({ ...c, channels: [...c.channels, ch] }));\n setDirtyWebhooks((d) => ({ ...d, [ch.id]: true }));\n }\n\n function updateRule(id: string, patch: Partial<Rule>) {\n setConfig((c) => ({\n ...c,\n rules: c.rules.map((r) => (r.id === id ? ({ ...r, ...patch } as Rule) : r)),\n }));\n }\n function removeRule(id: string) {\n setConfig((c) => ({ ...c, rules: c.rules.filter((r) => r.id !== id) }));\n }\n function addRule(kind: Rule['kind']) {\n setConfig((c) => ({ ...c, rules: [...c.rules, newRule(kind, c.channels[0]?.id ?? null)] }));\n }\n\n async function save() {\n setPending('save');\n setMessage(null);\n setError(null);\n try {\n const payload: Config = {\n ...config,\n channels: config.channels.map((c) => ({\n ...c,\n webhook: dirtyWebhooks[c.id] ? c.webhook : WEBHOOK_UNCHANGED_SENTINEL,\n })),\n };\n const res = await fetch('/api/settings/alerts', {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ config: payload }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error ?? 'save failed');\n setMessage(t('alerts.savedHint'));\n await refresh();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'save failed');\n } finally {\n setPending(null);\n }\n }\n\n async function runChannelAction(action: 'test' | 'send-now', channelId: string) {\n setPending(`${action}:${channelId}`);\n setMessage(null);\n setError(null);\n try {\n const res = await fetch('/api/settings/alerts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action, channelId }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error ?? 'request failed');\n if (data.result?.success) {\n const key = action === 'test' ? 'alerts.testSuccess' : 'alerts.summarySuccess';\n setMessage(t(key, { channel: data.result.channel, attempts: data.result.attempts ?? 1 }));\n } else {\n setError(t('alerts.pushFail', { message: data.result?.message ?? t('alerts.pushFailUnknown') }));\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'request failed');\n } finally {\n setPending(null);\n }\n }\n\n async function runAllRulesNow() {\n setPending('run-now');\n setMessage(null);\n setError(null);\n try {\n const res = await fetch('/api/settings/alerts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'run-now' }),\n });\n const data = await res.json();\n if (!res.ok) throw new Error(data.error ?? 'run failed');\n const fired = data.report?.fired ?? [];\n setMessage(t('alerts.evalDone', { fired: fired.length, evaluated: data.report?.evaluated ?? 0 }));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'run failed');\n } finally {\n setPending(null);\n }\n }\n\n const channelOptions = useMemo(() => config.channels.map((c) => ({ id: c.id, label: `${c.label} · ${c.type}` })), [config.channels]);\n\n return (\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-6\">\n <div className=\"mb-4 flex items-start justify-between gap-4\">\n <div>\n <h2 className=\"text-base font-semibold text-zinc-100\">{t('alerts.title')}</h2>\n <p className=\"text-zinc-500 text-xs mt-1\">\n {t('alerts.subtitle', { path: configPath, warn: '' })}\n <strong className=\"text-amber-300\">{t('alerts.subtitleWarn')}</strong>\n </p>\n </div>\n </div>\n\n {/* Channels */}\n <div className=\"mb-6\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-xs uppercase tracking-wider text-zinc-500\">{t('alerts.sectionChannels')}</h3>\n <div className=\"flex gap-2\">\n <button type=\"button\" onClick={() => addChannel('wxwork')} className=\"rounded border border-zinc-700 px-2 py-1 text-xs text-zinc-300 hover:border-zinc-500\">\n {t('alerts.addChannelWxwork')}\n </button>\n <button type=\"button\" onClick={() => addChannel('generic')} className=\"rounded border border-zinc-700 px-2 py-1 text-xs text-zinc-300 hover:border-zinc-500\">\n {t('alerts.addChannelGeneric')}\n </button>\n </div>\n </div>\n {config.channels.length === 0 ? (\n <p className=\"text-xs text-zinc-600 italic py-2\">{t('alerts.noChannels')}</p>\n ) : (\n <div className=\"space-y-3\">\n {config.channels.map((ch) => (\n <div key={ch.id} className=\"rounded border border-zinc-800 bg-zinc-950/40 p-3\">\n <div className=\"flex items-center justify-between gap-3 mb-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"rounded bg-zinc-800 px-2 py-0.5 text-[10px] uppercase tracking-wider text-zinc-400\">{ch.type}</span>\n <input\n type=\"text\"\n value={ch.label}\n onChange={(e) => updateChannel(ch.id, { label: e.target.value })}\n className=\"bg-transparent text-xs text-zinc-100 outline-none border-b border-transparent hover:border-zinc-700 focus:border-violet-500\"\n />\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => runChannelAction('test', ch.id)}\n disabled={pending !== null || dirtyWebhooks[ch.id]}\n title={dirtyWebhooks[ch.id] ? t('alerts.editFirst') : ''}\n className=\"rounded border border-zinc-700 px-2 py-1 text-[11px] text-zinc-300 hover:border-zinc-500 disabled:opacity-40\"\n >\n {pending === `test:${ch.id}` ? t('alerts.testing') : t('alerts.testBtn')}\n </button>\n <button\n type=\"button\"\n onClick={() => runChannelAction('send-now', ch.id)}\n disabled={pending !== null || dirtyWebhooks[ch.id]}\n className=\"rounded border border-zinc-700 px-2 py-1 text-[11px] text-zinc-300 hover:border-zinc-500 disabled:opacity-40\"\n >\n {pending === `send-now:${ch.id}` ? t('alerts.sending') : t('alerts.sendNow')}\n </button>\n <button\n type=\"button\"\n onClick={() => removeChannel(ch.id)}\n className=\"rounded border border-red-900/50 px-2 py-1 text-[11px] text-red-300 hover:border-red-700\"\n >\n {t('alerts.delete')}\n </button>\n </div>\n </div>\n <label className=\"block text-[11px] text-zinc-500 mb-1\">\n {t('alerts.webhookLabel')}{' '}\n {ch.type === 'wxwork' && <span className=\"text-zinc-600\">{t('alerts.webhookHintWxwork')}</span>}\n </label>\n <input\n type=\"text\"\n value={ch.webhook}\n onFocus={() => {\n if (!dirtyWebhooks[ch.id]) {\n updateChannel(ch.id, { webhook: '' });\n setDirtyWebhooks((d) => ({ ...d, [ch.id]: true }));\n }\n }}\n onChange={(e) => updateChannel(ch.id, { webhook: e.target.value })}\n placeholder={dirtyWebhooks[ch.id] ? t('alerts.webhookPlaceholderEdit') : t('alerts.webhookPlaceholderClick')}\n className=\"w-full rounded border border-zinc-800 bg-zinc-950 px-2 py-1.5 text-xs text-zinc-100 font-mono outline-none focus:border-violet-500\"\n />\n {!dirtyWebhooks[ch.id] && ch.webhook && (\n <p className=\"mt-1 text-[10px] text-zinc-600\">{t('alerts.webhookStored')}</p>\n )}\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Rules */}\n <div className=\"mb-6\">\n <div className=\"flex items-center justify-between mb-2\">\n <h3 className=\"text-xs uppercase tracking-wider text-zinc-500\">{t('alerts.sectionRules')}</h3>\n <div className=\"flex gap-2\">\n <button type=\"button\" onClick={() => addRule('threshold')} className=\"rounded border border-zinc-700 px-2 py-1 text-xs text-zinc-300 hover:border-zinc-500\">\n {t('alerts.addRuleThreshold')}\n </button>\n <button type=\"button\" onClick={() => addRule('daily')} className=\"rounded border border-zinc-700 px-2 py-1 text-xs text-zinc-300 hover:border-zinc-500\">\n {t('alerts.addRuleDaily')}\n </button>\n <button type=\"button\" onClick={() => addRule('reset_reminder')} className=\"rounded border border-zinc-700 px-2 py-1 text-xs text-zinc-300 hover:border-zinc-500\">\n {t('alerts.addRuleReset')}\n </button>\n </div>\n </div>\n {config.rules.length === 0 ? (\n <p className=\"text-xs text-zinc-600 italic py-2\">{t('alerts.noRules')}</p>\n ) : (\n <div className=\"space-y-3\">\n {config.rules.map((r) => (\n <RuleRow\n key={r.id}\n rule={r}\n channelOptions={channelOptions}\n onChange={(patch) => updateRule(r.id, patch)}\n onDelete={() => removeRule(r.id)}\n t={t}\n />\n ))}\n </div>\n )}\n </div>\n\n {/* Push language */}\n <div className=\"mb-4 flex items-center gap-3 text-xs\">\n <span className=\"text-zinc-500\">{t('alerts.pushLocale')}</span>\n <select\n value={config.pushLocale ?? 'zh'}\n onChange={(e) => setConfig((c) => ({ ...c, pushLocale: (e.target.value === 'en' ? 'en' : 'zh') as PushLocale }))}\n className=\"rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n >\n <option value=\"zh\">{t('alerts.pushLocaleZh')}</option>\n <option value=\"en\">{t('alerts.pushLocaleEn')}</option>\n </select>\n <span className=\"text-zinc-600 text-[10px]\">{t('alerts.pushLocaleHint')}</span>\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3 pt-2 border-t border-zinc-800\">\n <button\n type=\"button\"\n onClick={save}\n disabled={pending !== null}\n className=\"rounded-md bg-violet-600 px-3 py-2 text-xs text-white hover:bg-violet-500 disabled:opacity-50\"\n >\n {pending === 'save' ? t('alerts.saving') : t('alerts.saveBtn')}\n </button>\n <button\n type=\"button\"\n onClick={runAllRulesNow}\n disabled={pending !== null}\n className=\"rounded-md border border-zinc-700 px-3 py-2 text-xs text-zinc-300 hover:border-zinc-500 disabled:opacity-50\"\n >\n {pending === 'run-now' ? t('alerts.evaluating') : t('alerts.runAllNow')}\n </button>\n </div>\n\n {message && <p className=\"mt-3 text-xs text-emerald-400\">{message}</p>}\n {error && <p className=\"mt-3 text-xs text-red-400\">{error}</p>}\n </section>\n );\n}\n\nfunction RuleRow({\n rule,\n channelOptions,\n onChange,\n onDelete,\n t,\n}: {\n rule: Rule;\n channelOptions: { id: string; label: string }[];\n onChange: (patch: Partial<Rule>) => void;\n onDelete: () => void;\n t: (k: string, v?: Record<string, string | number>) => string;\n}) {\n const kindLabel = rule.kind === 'threshold' ? t('alerts.kindThreshold') : rule.kind === 'daily' ? t('alerts.kindDaily') : t('alerts.kindReset');\n\n return (\n <div className=\"rounded border border-zinc-800 bg-zinc-950/40 p-3\">\n <div className=\"flex items-center justify-between gap-3 mb-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"rounded bg-zinc-800 px-2 py-0.5 text-[10px] uppercase tracking-wider text-zinc-400\">{kindLabel}</span>\n <label className=\"flex items-center gap-1 text-xs text-zinc-400\">\n <input\n type=\"checkbox\"\n checked={rule.enabled}\n onChange={(e) => onChange({ enabled: e.target.checked })}\n className=\"h-3.5 w-3.5 accent-violet-500\"\n />\n <span>{t('alerts.enabled')}</span>\n </label>\n </div>\n <button type=\"button\" onClick={onDelete} className=\"rounded border border-red-900/50 px-2 py-1 text-[11px] text-red-300 hover:border-red-700\">\n {t('alerts.delete')}\n </button>\n </div>\n\n {rule.kind === 'threshold' && (\n <div className=\"grid sm:grid-cols-2 gap-2 text-xs\">\n <label className=\"flex items-center gap-2\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.metric')}</span>\n <select\n value={rule.metric}\n onChange={(e) => onChange({ metric: e.target.value as AlertMetric })}\n className=\"flex-1 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n >\n {METRIC_KEYS.map((v) => (\n <option key={v} value={v}>{t(`alerts.metric.${v}`)}</option>\n ))}\n </select>\n </label>\n <label className=\"flex items-center gap-2\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.below')}</span>\n <input\n type=\"number\" min={1} max={100} value={rule.below}\n onChange={(e) => onChange({ below: Number(e.target.value) })}\n className=\"w-20 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n />\n <span className=\"text-zinc-500\">%</span>\n </label>\n </div>\n )}\n\n {rule.kind === 'daily' && (\n <div className=\"flex items-center gap-2 text-xs\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.pushTime')}</span>\n <input\n type=\"number\" min={0} max={23} value={rule.hour}\n onChange={(e) => onChange({ hour: Number(e.target.value) })}\n className=\"w-16 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n />\n <span className=\"text-zinc-500\">:</span>\n <input\n type=\"number\" min={0} max={59} value={rule.minute}\n onChange={(e) => onChange({ minute: Number(e.target.value) })}\n className=\"w-16 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n />\n <span className=\"text-zinc-600 text-[10px] ml-2\">{t('alerts.pushTimeHint')}</span>\n </div>\n )}\n\n {rule.kind === 'reset_reminder' && (\n <div className=\"grid sm:grid-cols-3 gap-2 text-xs\">\n <label className=\"flex items-center gap-2\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.window')}</span>\n <select\n value={rule.metric}\n onChange={(e) => onChange({ metric: e.target.value as ResetMetric })}\n className=\"flex-1 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n >\n {RESET_METRIC_KEYS.map((v) => (\n <option key={v} value={v}>{t(`alerts.resetMetric.${v}`)}</option>\n ))}\n </select>\n </label>\n <label className=\"flex items-center gap-2\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.minutesBefore')}</span>\n <input\n type=\"number\" min={1} max={20160} value={rule.minutesBefore}\n onChange={(e) => onChange({ minutesBefore: Number(e.target.value) })}\n className=\"w-24 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n />\n <span className=\"text-zinc-500 text-[10px]\">{t('alerts.minutesBeforeHint')}</span>\n </label>\n <label className=\"flex items-center gap-2\">\n <span className=\"text-zinc-500 w-16 shrink-0\">{t('alerts.remainingAbove')}</span>\n <input\n type=\"number\" min={0} max={100} value={rule.remainingPctAbove}\n onChange={(e) => onChange({ remainingPctAbove: Number(e.target.value) })}\n className=\"w-20 rounded border border-zinc-800 bg-zinc-950 px-2 py-1 text-zinc-100 outline-none focus:border-violet-500\"\n />\n <span className=\"text-zinc-500 text-[10px]\">{t('alerts.remainingAboveHint')}</span>\n </label>\n </div>\n )}\n\n <div className=\"mt-2 text-xs\">\n <p className=\"text-zinc-500 mb-1\">{t('alerts.pushTo')}</p>\n {channelOptions.length === 0 ? (\n <p className=\"text-zinc-600 italic\">{t('alerts.addChannelFirst')}</p>\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {channelOptions.map((c) => {\n const on = rule.channelIds.includes(c.id);\n return (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => {\n const next = on ? rule.channelIds.filter((id) => id !== c.id) : [...rule.channelIds, c.id];\n onChange({ channelIds: next });\n }}\n className={`rounded-full border px-2 py-0.5 text-[11px] transition-colors ${\n on\n ? 'border-violet-500/60 bg-violet-500/10 text-violet-100'\n : 'border-zinc-700 text-zinc-400 hover:border-zinc-500'\n }`}\n >\n {c.label}\n </button>\n );\n })}\n </div>\n )}\n </div>\n </div>\n );\n}\n","/* eslint-disable @next/next/no-img-element */\n// QR PNGs are dropped in by the maintainer; next/image's optimizer would refuse\n// them when missing (no graceful onError) and we want the inline fallback path.\n'use client';\n\nimport { useState } from 'react';\nimport { useT } from '@/lib/i18n/client';\n\nconst ALIPAY_SRC = '/pay-alipay.jpg';\n\nexport function SettingsDonatePanel() {\n const t = useT();\n const [missing, setMissing] = useState(false);\n const [zoom, setZoom] = useState(false);\n const alipayLabel = t('donate.alipay');\n\n return (\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-6\">\n <div className=\"mb-4\">\n <h2 className=\"text-base font-semibold text-zinc-100\">{t('donate.title')}</h2>\n <p className=\"text-zinc-500 text-xs mt-1\">{t('donate.subtitle')}</p>\n </div>\n\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => !missing && setZoom(true)}\n className=\"w-48 rounded-md border border-zinc-800 bg-zinc-950/50 p-3 text-center transition-colors hover:border-zinc-700\"\n >\n <div className=\"aspect-square w-full overflow-hidden rounded bg-white\">\n {missing ? (\n <div className=\"flex h-full items-center justify-center px-2 text-center text-[10px] text-zinc-500\">\n {t('donate.missing', { path: 'public/pay-alipay.jpg' })}\n </div>\n ) : (\n <img\n src={ALIPAY_SRC}\n alt={alipayLabel}\n className=\"h-full w-full object-contain\"\n onError={() => setMissing(true)}\n />\n )}\n </div>\n <div className=\"mt-2 text-xs font-medium text-zinc-300\">{alipayLabel}</div>\n </button>\n <p className=\"flex-1 text-xs leading-relaxed text-zinc-500\">{t('donate.aside')}</p>\n </div>\n\n {missing && (\n <p className=\"mt-3 text-xs text-zinc-600\">\n {t('donate.missing', { path: 'public/pay-alipay.jpg' })}\n </p>\n )}\n\n {zoom && (\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center bg-black/80 p-6\"\n onClick={() => setZoom(false)}\n >\n <div className=\"rounded-lg bg-white p-4 text-center\" onClick={(e) => e.stopPropagation()}>\n <img src={ALIPAY_SRC} alt={alipayLabel} className=\"max-h-[70vh] max-w-[80vw] object-contain\" />\n <div className=\"mt-2 text-sm font-medium text-zinc-900\">{alipayLabel}</div>\n <button\n type=\"button\"\n onClick={() => setZoom(false)}\n className=\"mt-3 rounded border border-zinc-300 px-3 py-1 text-xs text-zinc-700 hover:bg-zinc-100\"\n >\n {t('donate.zoomClose')}\n </button>\n </div>\n </div>\n )}\n </section>\n );\n}\n"],"names":["SettingsNotifyPanel","initialStatus","t","status","setStatus","includeStop","setIncludeStop","includeNotification","setIncludeNotification","claudeNotification","includeCodex","setIncludeCodex","pending","setPending","message","setMessage","error","setError","enabled","claudeStop","codex","run","action","res","fetch","method","headers","body","JSON","stringify","stop","notification","payload","json","ok","Error","r","result","notes","codexSkipped","push","cmd","codexForeign","length","join","err","className","tool","project","StatusPill","label","on","warn","warnText","undefined","Toggle","checked","onChange","state","codexConfigExists","disabled","type","onClick","scriptExists","path","scriptPath","title","e","target","WEBHOOK_UNCHANGED_SENTINEL","METRIC_KEYS","RESET_METRIC_KEYS","newId","crypto","randomUUID","Math","random","toString","slice","Date","now","newChannel","defaultLabel","id","webhook","newRule","kind","firstChannelId","channelIds","metric","below","hour","minute","minutesBefore","remainingPctAbove","SettingsAlertsPanel","initialConfig","initialConfigPath","config","setConfig","dirtyWebhooks","setDirtyWebhooks","configPath","refresh","channels","rules","updateChannel","patch","c","map","ch","removeChannel","filter","cid","addChannel","d","updateRule","removeRule","addRule","save","data","runChannelAction","channelId","success","key","channel","attempts","runAllRulesNow","fired","report","evaluated","channelOptions","value","onFocus","placeholder","RuleRow","rule","onDelete","pushLocale","kindLabel","v","min","max","Number","includes","next","ALIPAY_SRC","SettingsDonatePanel","missing","setMissing","zoom","setZoom","alipayLabel","src","alt","onError","stopPropagation"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
module.exports=[95623,a=>{"use strict";var b=a.i(87924),c=a.i(72131);function d(a){if(!a)return
|
|
1
|
+
module.exports=[95623,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(42553);function e(a){return a.length>12?`${a.slice(0,8)}...${a.slice(-4)}`:a}a.s(["CodexAccountsPanel",0,function({initialAccounts:a}){let f=(0,d.useT)(),g=a=>(function(a,b){if(!a)return b;let c=new Date(a);return Number.isNaN(c.getTime())?b:c.toLocaleString(void 0,{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1})})(a,f("common.unknown")),[h,i]=(0,c.useState)(a),[j,k]=(0,c.useState)(null),[l,m]=(0,c.useState)(null),[n,o]=(0,c.useState)(null),p=(0,c.useMemo)(()=>h.find(a=>a.isCurrent)??null,[h]);async function q(a,b){k(b?`${a}:${b}`:a),m(null),o(null);try{let c=await fetch("/api/codex-accounts",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({action:a,accountId:b})}),d=await c.json();if(!c.ok)throw Error(d.error??"Operation failed");i(d.accounts??[]),"import-current"===a&&m(f("admin.importSuccess")),"switch"===a&&m(f("admin.switchSuccess")),"delete"===a&&m(f("admin.deleteSuccess"))}catch(a){o(a instanceof Error?a.message:"Operation failed")}finally{k(null)}}return(0,b.jsxs)("div",{className:"space-y-4",children:[(0,b.jsxs)("div",{className:"grid gap-4 lg:grid-cols-[1.2fr_0.8fr]",children:[(0,b.jsxs)("section",{className:"rounded-lg border border-zinc-800 bg-zinc-900 p-5",children:[(0,b.jsx)("p",{className:"text-xs uppercase tracking-wider text-zinc-500 mb-2",children:f("admin.activeAccountLabel")}),p?(0,b.jsxs)(b.Fragment,{children:[(0,b.jsx)("h2",{className:"text-lg font-semibold text-zinc-100",children:p.label}),(0,b.jsxs)("div",{className:"mt-4 grid grid-cols-2 gap-3 text-xs",children:[(0,b.jsxs)("div",{children:[(0,b.jsx)("p",{className:"text-zinc-600",children:f("admin.plan")}),(0,b.jsx)("p",{className:"mt-1 text-zinc-300",children:p.planType??f("common.unknown")})]}),(0,b.jsxs)("div",{children:[(0,b.jsx)("p",{className:"text-zinc-600",children:f("admin.accountId")}),(0,b.jsx)("p",{className:"mt-1 text-zinc-300",children:e(p.accountId)})]}),(0,b.jsxs)("div",{children:[(0,b.jsx)("p",{className:"text-zinc-600",children:f("admin.accessToken")}),(0,b.jsx)("p",{className:"mt-1 text-zinc-300",children:f("admin.expires",{date:g(p.accessTokenExpiresAt)})})]}),(0,b.jsxs)("div",{children:[(0,b.jsx)("p",{className:"text-zinc-600",children:f("admin.lastRefresh")}),(0,b.jsx)("p",{className:"mt-1 text-zinc-300",children:g(p.lastRefresh)})]})]})]}):(0,b.jsx)("p",{className:"text-sm text-zinc-500",children:f("admin.noAuth")})]}),(0,b.jsxs)("section",{className:"rounded-lg border border-zinc-800 bg-zinc-900 p-5",children:[(0,b.jsx)("p",{className:"text-xs uppercase tracking-wider text-zinc-500 mb-2",children:f("admin.localStore")}),(0,b.jsx)("p",{className:"text-sm text-zinc-400 leading-6",children:f("admin.localStoreHint",{path:"~/.codex/auth-accounts"})}),(0,b.jsx)("button",{type:"button",onClick:()=>q("import-current"),disabled:null!==j,className:"mt-4 w-full rounded-md border border-violet-500/40 bg-violet-500/15 px-3 py-2 text-xs font-medium text-violet-100 transition-colors hover:bg-violet-500/25 disabled:cursor-not-allowed disabled:opacity-50",children:"import-current"===j?f("admin.importing"):f("admin.importCurrent")})]})]}),(l||n)&&(0,b.jsx)("div",{className:`rounded-lg border px-4 py-3 text-xs ${n?"border-red-900/60 bg-red-950/40 text-red-200":"border-emerald-900/60 bg-emerald-950/30 text-emerald-200"}`,children:n??l}),(0,b.jsxs)("section",{className:"rounded-lg border border-zinc-800 bg-zinc-900",children:[(0,b.jsxs)("div",{className:"hidden grid-cols-[1.2fr_0.8fr_0.9fr_170px] border-b border-zinc-800 px-4 py-3 text-xs uppercase tracking-wider text-zinc-600 md:grid",children:[(0,b.jsx)("span",{children:f("admin.colAccount")}),(0,b.jsx)("span",{children:f("admin.colPlan")}),(0,b.jsx)("span",{children:f("admin.colTokens")}),(0,b.jsx)("span",{className:"text-right",children:f("admin.colActions")})]}),0===h.length?(0,b.jsx)("div",{className:"px-4 py-8 text-sm text-zinc-500",children:f("admin.noSaved")}):(0,b.jsx)("div",{className:"divide-y divide-zinc-800",children:h.map(a=>{let c=j===`switch:${a.accountId}`,d=j===`delete:${a.accountId}`;return(0,b.jsxs)("div",{className:"grid gap-3 px-4 py-4 md:grid-cols-[1.2fr_0.8fr_0.9fr_170px] md:items-center",children:[(0,b.jsxs)("div",{className:"min-w-0",children:[(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)("p",{className:"truncate text-sm font-medium text-zinc-100",children:a.label}),a.isCurrent&&(0,b.jsx)("span",{className:"rounded-full border border-emerald-500/40 px-2 py-0.5 text-[10px] uppercase tracking-wider text-emerald-300",children:f("admin.active")})]}),(0,b.jsx)("p",{className:"mt-1 truncate text-xs text-zinc-600",children:a.name??a.email??a.userId??e(a.accountId)})]}),(0,b.jsxs)("div",{className:"text-xs text-zinc-400",children:[(0,b.jsx)("span",{className:"mr-2 text-zinc-600 md:hidden",children:f("admin.plan")}),a.planType??f("common.unknown")]}),(0,b.jsxs)("div",{className:"text-xs text-zinc-500",children:[(0,b.jsx)("p",{children:f("admin.accessFmt",{date:g(a.accessTokenExpiresAt)})}),(0,b.jsx)("p",{className:"mt-1",children:f("admin.savedFmt",{date:g(a.storedAt)})})]}),(0,b.jsxs)("div",{className:"flex gap-2 md:justify-end",children:[(0,b.jsx)("button",{type:"button",onClick:()=>q("switch",a.accountId),disabled:a.isCurrent||null!==j,className:"rounded-md border border-zinc-700 px-3 py-1.5 text-xs text-zinc-200 transition-colors hover:border-zinc-500 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40",children:c?f("admin.switching"):f("admin.switch")}),(0,b.jsx)("button",{type:"button",onClick:()=>q("delete",a.accountId),disabled:a.isCurrent||null!==j,className:"rounded-md border border-zinc-800 px-3 py-1.5 text-xs text-zinc-500 transition-colors hover:border-red-900 hover:text-red-300 disabled:cursor-not-allowed disabled:opacity-30",children:d?f("admin.deleting"):f("common.delete")})]})]},a.accountId)})})]})]})}])}];
|
|
2
2
|
|
|
3
3
|
//# sourceMappingURL=src_components_CodexAccountsPanel_tsx_0n9xjzi._.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/CodexAccountsPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\n\ninterface CodexAccountSummary {\n accountId: string;\n label: string;\n email: string | null;\n name: string | null;\n planType: string | null;\n userId: string | null;\n lastRefresh: string | null;\n idTokenExpiresAt: string | null;\n accessTokenExpiresAt: string | null;\n storedAt: string | null;\n isCurrent: boolean;\n}\n\ninterface Props {\n initialAccounts: CodexAccountSummary[];\n}\n\nfunction formatDate(value: string | null) {\n if (!value) return 'unknown';\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return 'unknown';\n return date.toLocaleString('zh-CN', {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n });\n}\n\nfunction shortId(value: string) {\n return value.length > 12 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;\n}\n\nexport function CodexAccountsPanel({ initialAccounts }: Props) {\n const [accounts, setAccounts] = useState(initialAccounts);\n const [pending, setPending] = useState<string | null>(null);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const current = useMemo(() => accounts.find((account) => account.isCurrent) ?? null, [accounts]);\n\n async function runAction(action: string, accountId?: string) {\n setPending(accountId ? `${action}:${accountId}` : action);\n setMessage(null);\n setError(null);\n try {\n const response = await fetch('/api/codex-accounts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action, accountId }),\n });\n const payload = await response.json();\n if (!response.ok) throw new Error(payload.error ?? 'Operation failed');\n setAccounts(payload.accounts ?? []);\n if (action === 'import-current') setMessage('Current Codex login saved to the account list.');\n if (action === 'switch') setMessage('Codex auth.json switched. Restart active Codex sessions to use the new account.');\n if (action === 'delete') setMessage('Saved account removed.');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Operation failed');\n } finally {\n setPending(null);\n }\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 lg:grid-cols-[1.2fr_0.8fr]\">\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-5\">\n <p className=\"text-xs uppercase tracking-wider text-zinc-500 mb-2\">Active Codex account</p>\n {current ? (\n <>\n <h2 className=\"text-lg font-semibold text-zinc-100\">{current.label}</h2>\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-xs\">\n <div>\n <p className=\"text-zinc-600\">plan</p>\n <p className=\"mt-1 text-zinc-300\">{current.planType ?? 'unknown'}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">account id</p>\n <p className=\"mt-1 text-zinc-300\">{shortId(current.accountId)}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">access token</p>\n <p className=\"mt-1 text-zinc-300\">expires {formatDate(current.accessTokenExpiresAt)}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">last refresh</p>\n <p className=\"mt-1 text-zinc-300\">{formatDate(current.lastRefresh)}</p>\n </div>\n </div>\n </>\n ) : (\n <p className=\"text-sm text-zinc-500\">No readable Codex auth found at ~/.codex/auth.json.</p>\n )}\n </section>\n\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-5\">\n <p className=\"text-xs uppercase tracking-wider text-zinc-500 mb-2\">Local account store</p>\n <p className=\"text-sm text-zinc-400 leading-6\">\n Saved snapshots live in <span className=\"text-zinc-200\">~/.codex/auth-accounts</span>. Tokens are stored on disk for switching, but this page only renders decoded metadata.\n </p>\n <button\n type=\"button\"\n onClick={() => runAction('import-current')}\n disabled={pending !== null}\n className=\"mt-4 w-full rounded-md border border-violet-500/40 bg-violet-500/15 px-3 py-2 text-xs font-medium text-violet-100 transition-colors hover:bg-violet-500/25 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {pending === 'import-current' ? 'Saving...' : 'Save current Codex login'}\n </button>\n </section>\n </div>\n\n {(message || error) && (\n <div className={`rounded-lg border px-4 py-3 text-xs ${error ? 'border-red-900/60 bg-red-950/40 text-red-200' : 'border-emerald-900/60 bg-emerald-950/30 text-emerald-200'}`}>\n {error ?? message}\n </div>\n )}\n\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900\">\n <div className=\"hidden grid-cols-[1.2fr_0.8fr_0.9fr_170px] border-b border-zinc-800 px-4 py-3 text-xs uppercase tracking-wider text-zinc-600 md:grid\">\n <span>account</span>\n <span>plan</span>\n <span>tokens</span>\n <span className=\"text-right\">actions</span>\n </div>\n {accounts.length === 0 ? (\n <div className=\"px-4 py-8 text-sm text-zinc-500\">No saved accounts yet. Log into Codex, then save the current login here.</div>\n ) : (\n <div className=\"divide-y divide-zinc-800\">\n {accounts.map((account) => {\n const switchPending = pending === `switch:${account.accountId}`;\n const deletePending = pending === `delete:${account.accountId}`;\n return (\n <div key={account.accountId} className=\"grid gap-3 px-4 py-4 md:grid-cols-[1.2fr_0.8fr_0.9fr_170px] md:items-center\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium text-zinc-100\">{account.label}</p>\n {account.isCurrent && <span className=\"rounded-full border border-emerald-500/40 px-2 py-0.5 text-[10px] uppercase tracking-wider text-emerald-300\">active</span>}\n </div>\n <p className=\"mt-1 truncate text-xs text-zinc-600\">{account.name ?? account.email ?? account.userId ?? shortId(account.accountId)}</p>\n </div>\n <div className=\"text-xs text-zinc-400\">\n <span className=\"mr-2 text-zinc-600 md:hidden\">plan</span>\n {account.planType ?? 'unknown'}\n </div>\n <div className=\"text-xs text-zinc-500\">\n <p>access {formatDate(account.accessTokenExpiresAt)}</p>\n <p className=\"mt-1\">saved {formatDate(account.storedAt)}</p>\n </div>\n <div className=\"flex gap-2 md:justify-end\">\n <button\n type=\"button\"\n onClick={() => runAction('switch', account.accountId)}\n disabled={account.isCurrent || pending !== null}\n className=\"rounded-md border border-zinc-700 px-3 py-1.5 text-xs text-zinc-200 transition-colors hover:border-zinc-500 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {switchPending ? 'Switching...' : 'Switch'}\n </button>\n <button\n type=\"button\"\n onClick={() => runAction('delete', account.accountId)}\n disabled={account.isCurrent || pending !== null}\n className=\"rounded-md border border-zinc-800 px-3 py-1.5 text-xs text-zinc-500 transition-colors hover:border-red-900 hover:text-red-300 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n {deletePending ? 'Deleting...' : 'Delete'}\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n );\n}\n"],"names":["formatDate","value","date","Date","Number","isNaN","getTime","toLocaleString","month","day","hour","minute","hour12","shortId","length","slice","CodexAccountsPanel","initialAccounts","accounts","setAccounts","pending","setPending","message","setMessage","error","setError","current","find","account","isCurrent","runAction","action","accountId","response","fetch","method","headers","body","JSON","stringify","payload","json","ok","Error","err","className","label","planType","accessTokenExpiresAt","lastRefresh","type","onClick","disabled","map","switchPending","deletePending","name","email","userId","storedAt"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/CodexAccountsPanel.tsx"],"sourcesContent":["'use client';\n\nimport { useMemo, useState } from 'react';\nimport { useT } from '@/lib/i18n/client';\n\ninterface CodexAccountSummary {\n accountId: string;\n label: string;\n email: string | null;\n name: string | null;\n planType: string | null;\n userId: string | null;\n lastRefresh: string | null;\n idTokenExpiresAt: string | null;\n accessTokenExpiresAt: string | null;\n storedAt: string | null;\n isCurrent: boolean;\n}\n\ninterface Props {\n initialAccounts: CodexAccountSummary[];\n}\n\nfunction formatDate(value: string | null, unknownLabel: string) {\n if (!value) return unknownLabel;\n const date = new Date(value);\n if (Number.isNaN(date.getTime())) return unknownLabel;\n return date.toLocaleString(undefined, {\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n hour12: false,\n });\n}\n\nfunction shortId(value: string) {\n return value.length > 12 ? `${value.slice(0, 8)}...${value.slice(-4)}` : value;\n}\n\nexport function CodexAccountsPanel({ initialAccounts }: Props) {\n const t = useT();\n const fd = (v: string | null) => formatDate(v, t('common.unknown'));\n const [accounts, setAccounts] = useState(initialAccounts);\n const [pending, setPending] = useState<string | null>(null);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const current = useMemo(() => accounts.find((account) => account.isCurrent) ?? null, [accounts]);\n\n async function runAction(action: string, accountId?: string) {\n setPending(accountId ? `${action}:${accountId}` : action);\n setMessage(null);\n setError(null);\n try {\n const response = await fetch('/api/codex-accounts', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action, accountId }),\n });\n const payload = await response.json();\n if (!response.ok) throw new Error(payload.error ?? 'Operation failed');\n setAccounts(payload.accounts ?? []);\n if (action === 'import-current') setMessage(t('admin.importSuccess'));\n if (action === 'switch') setMessage(t('admin.switchSuccess'));\n if (action === 'delete') setMessage(t('admin.deleteSuccess'));\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Operation failed');\n } finally {\n setPending(null);\n }\n }\n\n return (\n <div className=\"space-y-4\">\n <div className=\"grid gap-4 lg:grid-cols-[1.2fr_0.8fr]\">\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-5\">\n <p className=\"text-xs uppercase tracking-wider text-zinc-500 mb-2\">{t('admin.activeAccountLabel')}</p>\n {current ? (\n <>\n <h2 className=\"text-lg font-semibold text-zinc-100\">{current.label}</h2>\n <div className=\"mt-4 grid grid-cols-2 gap-3 text-xs\">\n <div>\n <p className=\"text-zinc-600\">{t('admin.plan')}</p>\n <p className=\"mt-1 text-zinc-300\">{current.planType ?? t('common.unknown')}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">{t('admin.accountId')}</p>\n <p className=\"mt-1 text-zinc-300\">{shortId(current.accountId)}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">{t('admin.accessToken')}</p>\n <p className=\"mt-1 text-zinc-300\">{t('admin.expires', { date: fd(current.accessTokenExpiresAt) })}</p>\n </div>\n <div>\n <p className=\"text-zinc-600\">{t('admin.lastRefresh')}</p>\n <p className=\"mt-1 text-zinc-300\">{fd(current.lastRefresh)}</p>\n </div>\n </div>\n </>\n ) : (\n <p className=\"text-sm text-zinc-500\">{t('admin.noAuth')}</p>\n )}\n </section>\n\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900 p-5\">\n <p className=\"text-xs uppercase tracking-wider text-zinc-500 mb-2\">{t('admin.localStore')}</p>\n <p className=\"text-sm text-zinc-400 leading-6\">\n {t('admin.localStoreHint', { path: '~/.codex/auth-accounts' })}\n </p>\n <button\n type=\"button\"\n onClick={() => runAction('import-current')}\n disabled={pending !== null}\n className=\"mt-4 w-full rounded-md border border-violet-500/40 bg-violet-500/15 px-3 py-2 text-xs font-medium text-violet-100 transition-colors hover:bg-violet-500/25 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {pending === 'import-current' ? t('admin.importing') : t('admin.importCurrent')}\n </button>\n </section>\n </div>\n\n {(message || error) && (\n <div className={`rounded-lg border px-4 py-3 text-xs ${error ? 'border-red-900/60 bg-red-950/40 text-red-200' : 'border-emerald-900/60 bg-emerald-950/30 text-emerald-200'}`}>\n {error ?? message}\n </div>\n )}\n\n <section className=\"rounded-lg border border-zinc-800 bg-zinc-900\">\n <div className=\"hidden grid-cols-[1.2fr_0.8fr_0.9fr_170px] border-b border-zinc-800 px-4 py-3 text-xs uppercase tracking-wider text-zinc-600 md:grid\">\n <span>{t('admin.colAccount')}</span>\n <span>{t('admin.colPlan')}</span>\n <span>{t('admin.colTokens')}</span>\n <span className=\"text-right\">{t('admin.colActions')}</span>\n </div>\n {accounts.length === 0 ? (\n <div className=\"px-4 py-8 text-sm text-zinc-500\">{t('admin.noSaved')}</div>\n ) : (\n <div className=\"divide-y divide-zinc-800\">\n {accounts.map((account) => {\n const switchPending = pending === `switch:${account.accountId}`;\n const deletePending = pending === `delete:${account.accountId}`;\n return (\n <div key={account.accountId} className=\"grid gap-3 px-4 py-4 md:grid-cols-[1.2fr_0.8fr_0.9fr_170px] md:items-center\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium text-zinc-100\">{account.label}</p>\n {account.isCurrent && <span className=\"rounded-full border border-emerald-500/40 px-2 py-0.5 text-[10px] uppercase tracking-wider text-emerald-300\">{t('admin.active')}</span>}\n </div>\n <p className=\"mt-1 truncate text-xs text-zinc-600\">{account.name ?? account.email ?? account.userId ?? shortId(account.accountId)}</p>\n </div>\n <div className=\"text-xs text-zinc-400\">\n <span className=\"mr-2 text-zinc-600 md:hidden\">{t('admin.plan')}</span>\n {account.planType ?? t('common.unknown')}\n </div>\n <div className=\"text-xs text-zinc-500\">\n <p>{t('admin.accessFmt', { date: fd(account.accessTokenExpiresAt) })}</p>\n <p className=\"mt-1\">{t('admin.savedFmt', { date: fd(account.storedAt) })}</p>\n </div>\n <div className=\"flex gap-2 md:justify-end\">\n <button\n type=\"button\"\n onClick={() => runAction('switch', account.accountId)}\n disabled={account.isCurrent || pending !== null}\n className=\"rounded-md border border-zinc-700 px-3 py-1.5 text-xs text-zinc-200 transition-colors hover:border-zinc-500 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40\"\n >\n {switchPending ? t('admin.switching') : t('admin.switch')}\n </button>\n <button\n type=\"button\"\n onClick={() => runAction('delete', account.accountId)}\n disabled={account.isCurrent || pending !== null}\n className=\"rounded-md border border-zinc-800 px-3 py-1.5 text-xs text-zinc-500 transition-colors hover:border-red-900 hover:text-red-300 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n {deletePending ? t('admin.deleting') : t('common.delete')}\n </button>\n </div>\n </div>\n );\n })}\n </div>\n )}\n </section>\n </div>\n );\n}\n"],"names":["formatDate","value","unknownLabel","date","Date","Number","isNaN","getTime","toLocaleString","undefined","month","day","hour","minute","hour12","shortId","length","slice","CodexAccountsPanel","initialAccounts","t","fd","v","accounts","setAccounts","pending","setPending","message","setMessage","error","setError","current","find","account","isCurrent","runAction","action","accountId","response","fetch","method","headers","body","JSON","stringify","payload","json","ok","Error","err","className","label","planType","accessTokenExpiresAt","lastRefresh","path","type","onClick","disabled","map","switchPending","deletePending","name","email","userId","storedAt"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
module.exports=[4786,a=>{"use strict";var b=a.i(87924),c=a.i(72131),d=a.i(42553);function e(a){return"claude-code"===a?"Claude":"codex"===a?"Codex":"cursor"===a?"Cursor":a}function f(a){if(null==a)return"--";let b=Math.max(0,Math.min(100,a));return`${b>=100?100:Math.floor(b)}%`}a.s(["FloatingWidget",0,function({initialStats:a}){let g,h=(0,d.useT)(),[i,j]=(0,c.useState)(a),[k,l]=(0,c.useState)(!0),[m,n]=(0,c.useState)(!1),o=i.primary,p=o?.remaining5h??o?.remainingWeekly??null,q=o?.used5h??o?.usedWeekly??0,r=null==(g=o?.remaining5h??o?.remainingWeekly??null)?"#71717a":g<20?"#f43f5e":g<45?"#f59e0b":"#10b981",s=(0,c.useMemo)(()=>{let a=Math.max(0,Math.min(100,q));return`conic-gradient(${r} 0 ${100-a}%, #27272a ${100-a}% 100%)`},[r,q]);async function t(){let a=await fetch("/api/float",{cache:"no-store"});a.ok&&j(await a.json())}async function u(){n(!0);try{await fetch("/api/import-sessions",{method:"POST"}),await t()}finally{n(!1)}}return(0,c.useEffect)(()=>{window.resizeTo?.(260,k?380:220);let a=window.setInterval(t,6e4);return()=>window.clearInterval(a)},[k]),(0,b.jsx)("main",{className:"min-h-screen overflow-hidden bg-zinc-950 text-zinc-100",children:(0,b.jsxs)("div",{className:"mx-auto flex min-h-screen w-full max-w-[280px] flex-col items-center justify-center gap-3 px-4 py-4 font-mono",children:[(0,b.jsx)("button",{type:"button","aria-label":"Toggle details",onClick:()=>l(a=>!a),className:"relative grid size-40 place-items-center rounded-full border border-zinc-800 bg-zinc-950 shadow-2xl shadow-black/60 outline-none transition-transform hover:scale-[1.02]",style:{background:s},children:(0,b.jsx)("span",{className:"grid size-[8.75rem] place-items-center rounded-full border border-zinc-800 bg-zinc-950",children:(0,b.jsxs)("span",{className:"text-center",children:[(0,b.jsx)("span",{className:"block text-[11px] uppercase tracking-[0.18em] text-zinc-500",children:o?.label??"Vibemeter"}),(0,b.jsx)("span",{className:"mt-1 block text-4xl font-semibold text-zinc-50",children:f(p)}),(0,b.jsx)("span",{className:"mt-1 block text-[11px] text-zinc-500",children:h(o?"float.fiveHRemain":"float.noQuota")})]})})}),(0,b.jsxs)("div",{className:"flex items-center gap-2",children:[(0,b.jsx)("button",{type:"button",onClick:u,disabled:m,className:"rounded-full border border-violet-500/40 bg-violet-500/10 px-3 py-1.5 text-xs text-violet-100 transition-colors hover:bg-violet-500/20 disabled:opacity-50",children:h(m?"float.refreshing":"float.refresh")}),(0,b.jsx)("a",{href:"/",target:"_blank",className:"rounded-full border border-zinc-700 px-3 py-1.5 text-xs text-zinc-300 transition-colors hover:border-zinc-500 hover:text-zinc-100",children:h("common.dashboard")})]}),k&&(0,b.jsxs)("section",{className:"w-full rounded-lg border border-zinc-800 bg-zinc-900/90 p-3",children:[(0,b.jsxs)("div",{className:"flex items-start justify-between gap-3",children:[(0,b.jsxs)("div",{className:"min-w-0",children:[(0,b.jsx)("p",{className:"truncate text-xs text-zinc-500",children:o?.accountLabel??h("float.currentQuota")}),(0,b.jsx)("p",{className:"mt-1 text-sm font-medium text-zinc-100",children:o?h("float.usedPct",{pct:function(a){if(null==a)return"--";let b=Math.max(0,Math.min(100,a));return`${b<=0?0:Math.ceil(b)}%`}(o.used5h)}):h("float.usedNo")})]}),(0,b.jsxs)("div",{className:"text-right text-xs text-zinc-500",children:[(0,b.jsx)("p",{children:function(a,b){if(!a)return b("float.unknownReset");let c=a-Date.now();if(c<=0)return b("float.snapshotExpired");let d=Math.floor(c/36e5),e=Math.floor(c%36e5/6e4);return d>0?`${d}h ${e}m`:`${e}m`}(o?.resetAt5h??null,h)}),(0,b.jsx)("p",{className:"mt-1",children:function(a,b){if(!a)return b("float.noSnapshot");let c=Math.floor(Math.max(0,Date.now()-a)/6e4);return c<1?b("float.justNow"):c<60?b("float.minAgo",{n:c}):b("float.hourAgo",{n:Math.floor(c/60)})}(o?.capturedAt??null,h)})]})]}),(0,b.jsxs)("div",{className:"mt-3 grid grid-cols-3 gap-2",children:[(0,b.jsxs)("div",{className:"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2",children:[(0,b.jsx)("p",{className:"text-[10px] uppercase tracking-wider text-zinc-600",children:h("float.statToday")}),(0,b.jsx)("p",{className:"mt-1 text-lg font-semibold text-zinc-100",children:i.todaySessions})]}),(0,b.jsxs)("div",{className:"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2",children:[(0,b.jsx)("p",{className:"text-[10px] uppercase tracking-wider text-zinc-600",children:h("float.statTotal")}),(0,b.jsx)("p",{className:"mt-1 text-lg font-semibold text-zinc-100",children:i.totalSessions})]}),(0,b.jsxs)("div",{className:"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2",children:[(0,b.jsx)("p",{className:"text-[10px] uppercase tracking-wider text-zinc-600",children:h("float.statWeekly")}),(0,b.jsx)("p",{className:"mt-1 text-lg font-semibold text-zinc-100",children:f(o?.remainingWeekly)})]})]}),(0,b.jsxs)("div",{className:"mt-3 space-y-2",children:[i.todayByTool.slice(0,3).map(a=>(0,b.jsxs)("div",{className:"flex items-center justify-between text-xs",children:[(0,b.jsx)("span",{className:"text-zinc-500",children:e(a.tool)}),(0,b.jsx)("span",{className:"text-zinc-300",children:a.count})]},a.tool)),0===i.todayByTool.length&&(0,b.jsx)("p",{className:"text-xs text-zinc-600",children:h("float.noToday")})]}),i.lastSession&&(0,b.jsxs)("div",{className:"mt-3 border-t border-zinc-800 pt-3",children:[(0,b.jsx)("p",{className:"text-[10px] uppercase tracking-wider text-zinc-600",children:h("float.latest")}),(0,b.jsxs)("p",{className:"mt-1 truncate text-xs text-zinc-300",children:[e(i.lastSession.tool)," · ",i.lastSession.project]}),i.lastSession.title&&(0,b.jsx)("p",{className:"mt-1 line-clamp-2 text-xs text-zinc-500",children:i.lastSession.title})]})]})]})})}])}];
|
|
2
|
+
|
|
3
|
+
//# sourceMappingURL=src_components_FloatingWidget_tsx_089.6oo._.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/FloatingWidget.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState } from 'react';\nimport type { FloatQuota, FloatStats } from '@/lib/float-stats';\nimport { useT } from '@/lib/i18n/client';\n\nfunction formatReset(ms: number | null, t: (k: string, v?: Record<string, string | number>) => string) {\n if (!ms) return t('float.unknownReset');\n const diff = ms - Date.now();\n if (diff <= 0) return t('float.snapshotExpired');\n const hours = Math.floor(diff / 3_600_000);\n const minutes = Math.floor((diff % 3_600_000) / 60_000);\n if (hours > 0) return `${hours}h ${minutes}m`;\n return `${minutes}m`;\n}\n\nfunction formatAge(ms: number | null, t: (k: string, v?: Record<string, string | number>) => string) {\n if (!ms) return t('float.noSnapshot');\n const diff = Math.max(0, Date.now() - ms);\n const minutes = Math.floor(diff / 60_000);\n if (minutes < 1) return t('float.justNow');\n if (minutes < 60) return t('float.minAgo', { n: minutes });\n return t('float.hourAgo', { n: Math.floor(minutes / 60) });\n}\n\nfunction toolLabel(tool: string) {\n if (tool === 'claude-code') return 'Claude';\n if (tool === 'codex') return 'Codex';\n if (tool === 'cursor') return 'Cursor';\n return tool;\n}\n\nfunction quotaColor(quota: FloatQuota | null) {\n const remaining = quota?.remaining5h ?? quota?.remainingWeekly ?? null;\n if (remaining == null) return '#71717a';\n if (remaining < 20) return '#f43f5e';\n if (remaining < 45) return '#f59e0b';\n return '#10b981';\n}\n\nfunction formatRemainingPercent(value: number | null | undefined) {\n if (value == null) return '--';\n const clamped = Math.max(0, Math.min(100, value));\n return `${clamped >= 100 ? 100 : Math.floor(clamped)}%`;\n}\n\nfunction formatUsedPercent(value: number | null | undefined) {\n if (value == null) return '--';\n const clamped = Math.max(0, Math.min(100, value));\n return `${clamped <= 0 ? 0 : Math.ceil(clamped)}%`;\n}\n\nexport function FloatingWidget({ initialStats }: { initialStats: FloatStats }) {\n const t = useT();\n const [stats, setStats] = useState(initialStats);\n const [expanded, setExpanded] = useState(true);\n const [busy, setBusy] = useState(false);\n\n const primary = stats.primary;\n const remaining = primary?.remaining5h ?? primary?.remainingWeekly ?? null;\n const used = primary?.used5h ?? primary?.usedWeekly ?? 0;\n const color = quotaColor(primary);\n const ring = useMemo(() => {\n const pct = Math.max(0, Math.min(100, used));\n return `conic-gradient(${color} 0 ${100 - pct}%, #27272a ${100 - pct}% 100%)`;\n }, [color, used]);\n\n async function loadStats() {\n const response = await fetch('/api/float', { cache: 'no-store' });\n if (response.ok) setStats(await response.json());\n }\n\n async function refresh() {\n setBusy(true);\n try {\n await fetch('/api/import-sessions', { method: 'POST' });\n await loadStats();\n } finally {\n setBusy(false);\n }\n }\n\n useEffect(() => {\n window.resizeTo?.(260, expanded ? 380 : 220);\n const timer = window.setInterval(loadStats, 60_000);\n return () => window.clearInterval(timer);\n }, [expanded]);\n\n return (\n <main className=\"min-h-screen overflow-hidden bg-zinc-950 text-zinc-100\">\n <div className=\"mx-auto flex min-h-screen w-full max-w-[280px] flex-col items-center justify-center gap-3 px-4 py-4 font-mono\">\n <button\n type=\"button\"\n aria-label=\"Toggle details\"\n onClick={() => setExpanded((value) => !value)}\n className=\"relative grid size-40 place-items-center rounded-full border border-zinc-800 bg-zinc-950 shadow-2xl shadow-black/60 outline-none transition-transform hover:scale-[1.02]\"\n style={{ background: ring }}\n >\n <span className=\"grid size-[8.75rem] place-items-center rounded-full border border-zinc-800 bg-zinc-950\">\n <span className=\"text-center\">\n <span className=\"block text-[11px] uppercase tracking-[0.18em] text-zinc-500\">\n {primary?.label ?? 'Vibemeter'}\n </span>\n <span className=\"mt-1 block text-4xl font-semibold text-zinc-50\">\n {formatRemainingPercent(remaining)}\n </span>\n <span className=\"mt-1 block text-[11px] text-zinc-500\">\n {primary ? t('float.fiveHRemain') : t('float.noQuota')}\n </span>\n </span>\n </span>\n </button>\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={refresh}\n disabled={busy}\n className=\"rounded-full border border-violet-500/40 bg-violet-500/10 px-3 py-1.5 text-xs text-violet-100 transition-colors hover:bg-violet-500/20 disabled:opacity-50\"\n >\n {busy ? t('float.refreshing') : t('float.refresh')}\n </button>\n <a\n href=\"/\"\n target=\"_blank\"\n className=\"rounded-full border border-zinc-700 px-3 py-1.5 text-xs text-zinc-300 transition-colors hover:border-zinc-500 hover:text-zinc-100\"\n >\n {t('common.dashboard')}\n </a>\n </div>\n\n {expanded && (\n <section className=\"w-full rounded-lg border border-zinc-800 bg-zinc-900/90 p-3\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <p className=\"truncate text-xs text-zinc-500\">\n {primary?.accountLabel ?? t('float.currentQuota')}\n </p>\n <p className=\"mt-1 text-sm font-medium text-zinc-100\">\n {primary ? t('float.usedPct', { pct: formatUsedPercent(primary.used5h) }) : t('float.usedNo')}\n </p>\n </div>\n <div className=\"text-right text-xs text-zinc-500\">\n <p>{formatReset(primary?.resetAt5h ?? null, t)}</p>\n <p className=\"mt-1\">{formatAge(primary?.capturedAt ?? null, t)}</p>\n </div>\n </div>\n\n <div className=\"mt-3 grid grid-cols-3 gap-2\">\n <div className=\"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2\">\n <p className=\"text-[10px] uppercase tracking-wider text-zinc-600\">{t('float.statToday')}</p>\n <p className=\"mt-1 text-lg font-semibold text-zinc-100\">{stats.todaySessions}</p>\n </div>\n <div className=\"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2\">\n <p className=\"text-[10px] uppercase tracking-wider text-zinc-600\">{t('float.statTotal')}</p>\n <p className=\"mt-1 text-lg font-semibold text-zinc-100\">{stats.totalSessions}</p>\n </div>\n <div className=\"rounded-md border border-zinc-800 bg-zinc-950 px-2 py-2\">\n <p className=\"text-[10px] uppercase tracking-wider text-zinc-600\">{t('float.statWeekly')}</p>\n <p className=\"mt-1 text-lg font-semibold text-zinc-100\">{formatRemainingPercent(primary?.remainingWeekly)}</p>\n </div>\n </div>\n\n <div className=\"mt-3 space-y-2\">\n {stats.todayByTool.slice(0, 3).map((item) => (\n <div key={item.tool} className=\"flex items-center justify-between text-xs\">\n <span className=\"text-zinc-500\">{toolLabel(item.tool)}</span>\n <span className=\"text-zinc-300\">{item.count}</span>\n </div>\n ))}\n {stats.todayByTool.length === 0 && (\n <p className=\"text-xs text-zinc-600\">{t('float.noToday')}</p>\n )}\n </div>\n\n {stats.lastSession && (\n <div className=\"mt-3 border-t border-zinc-800 pt-3\">\n <p className=\"text-[10px] uppercase tracking-wider text-zinc-600\">{t('float.latest')}</p>\n <p className=\"mt-1 truncate text-xs text-zinc-300\">\n {toolLabel(stats.lastSession.tool)} · {stats.lastSession.project}\n </p>\n {stats.lastSession.title && (\n <p className=\"mt-1 line-clamp-2 text-xs text-zinc-500\">{stats.lastSession.title}</p>\n )}\n </div>\n )}\n </section>\n )}\n </div>\n </main>\n );\n}\n"],"names":["formatReset","ms","t","diff","Date","now","hours","Math","floor","minutes","formatAge","max","n","toolLabel","tool","quotaColor","quota","remaining","remaining5h","remainingWeekly","formatRemainingPercent","value","clamped","min","formatUsedPercent","ceil","FloatingWidget","initialStats","stats","setStats","expanded","setExpanded","busy","setBusy","primary","used","used5h","usedWeekly","color","ring","pct","loadStats","response","fetch","cache","ok","json","refresh","method","window","resizeTo","timer","setInterval","clearInterval","className","type","onClick","style","background","label","disabled","href","target","accountLabel","resetAt5h","capturedAt","todaySessions","totalSessions","todayByTool","slice","map","item","count","length","lastSession","project","title"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
module.exports=[42553,96130,a=>{"use strict";var b=a.i(87924),c=a.i(72131);a.s(["DEFAULT_LOCALE",0,"zh","LOCALE_COOKIE",0,"vibemeter_locale"],96130);let d={zh:{"common.dashboard":"仪表盘","common.admin":"管理","common.settings":"设置","common.cancel":"取消","common.save":"保存","common.delete":"删除","common.close":"关闭","common.add":"添加","common.edit":"编辑","common.refresh":"刷新","common.loading":"加载中…","common.unknown":"未知","common.all":"全部","common.empty":"暂无数据","common.language":"语言","header.tagline":"量化你的 AI 编码节奏 · 本地优先 · 数据永不离开本机","header.langZh":"中文","header.langEn":"EN","dashboard.refreshData":"刷新数据","dashboard.refreshing":"刷新中…","dashboard.toolAll":"全部","dashboard.toolClaude":"Claude Code","dashboard.toolCodex":"Codex","dashboard.toolCursor":"Cursor","dashboard.dateToday":"今天","dashboard.date7d":"近 7 天","dashboard.date30d":"近 30 天","dashboard.dateAll":"全部","dashboard.codexAccount":"Codex 账户","dashboard.codexAccountAll":"所有 Codex 账户","dashboard.codexHistoryFilter.current":"仅当前账户","dashboard.codexHistoryFilter.saved":"仅已保存账户的历史","dashboard.codexHistoryFilter.all":"所有已保存与历史 Codex 使用","dashboard.codexSwitchHint":"当前选项只筛选历史。如要采集新数据,请到「管理」切换账户。","dashboard.window5h":"5h 窗口","dashboard.windowWeekly":"本周窗口","dashboard.remaining":"剩余","dashboard.used":"已用","dashboard.noData":"暂无数据","dashboard.todaySessions":"今日会话","dashboard.totalSessions":"累计会话","dashboard.dateToday2":"今天","dashboard.date7days":"近 7 天","dashboard.date30days":"近 30 天","dashboard.dateAlltime":"全部时间","dashboard.refreshFailed":"刷新失败","dashboard.scanned":"扫了 {n} 条日志,耗时 {seconds}s","dashboard.codexCurrentOnly":"仅当前账户","dashboard.codexSavedOnly":"仅已保存账户的历史","dashboard.codexAllUsage":"所有已保存与历史 Codex 使用","dashboard.window7d":"7 天预算","dashboard.codexLabel":"codex · 所有账户","dashboard.codexLabelPrefix":"codex","dashboard.allAgents":"所有 agents","dashboard.claudeCodeLower":"claude code","dashboard.noSnapshot":"该账户暂无 rate-limit 快照","dashboard.useCodexThenRefresh":"在 Codex 用一次再刷新","dashboard.switchAdminThenUse":"到「管理」切换后用一次 Codex,再刷新","dashboard.resetting":"重置中…","dashboard.resetsAt":"{date}({rel}后)重置","dashboard.short":"all","card.spending.title":"消费 / 用量","card.spending.claudeCost":"claude code 累计","card.spending.codexTokens":"codex tokens","card.spending.last14d":"近 14 天","card.spending.noCursor":"当前筛选下无消费数据(cursor 无 token 计费)","card.spending.legendClaude":"claude ($)","card.spending.legendCodex":"codex (tokens)","card.activity.title":"活跃度","card.activity.streakDays":"连续 {n} 天","card.activity.peakSlot":"高峰时段","card.activity.todayTimeline":"今日时间线","card.activity.viewPattern":"热力图","card.activity.viewToday":"今日","card.activity.dayStreak":"天连续","card.activity.longest":"最长","card.activity.activeDays":"活跃天","card.activity.total":"累计","card.activity.peakSlotFmt":"高峰时段:{day} {hour}:00({mins})","card.activity.dayMon":"一","card.activity.dayTue":"二","card.activity.dayWed":"三","card.activity.dayThu":"四","card.activity.dayFri":"五","card.activity.daySat":"六","card.activity.daySun":"日","card.activity.less":"少","card.activity.more":"多","card.activity.dayHeader":"{date} · {n} 次会话 · 累计 {hours}h","card.activity.noToday":"今天还没有会话","card.achievements.title":"成就","card.achievements.unlocked":"已解锁 {n} / {total}","card.achievements.showLess":"收起","card.achievements.showAll":"查看全部({n})","achv.sessions-100.title":"百战不殆","achv.sessions-100.desc":"累计 100 个会话","achv.sessions-500.title":"已成习惯","achv.sessions-500.desc":"累计 500 个会话","achv.sessions-1000.title":"千锤百炼","achv.sessions-1000.desc":"累计 1000 个会话","achv.streak-7.title":"一周连击","achv.streak-7.desc":"连续编码 7 天","achv.streak-30.title":"月度坚持","achv.streak-30.desc":"连续编码 30 天","achv.spend-10.title":"小试牛刀","achv.spend-10.desc":"Claude 累计花费 $10+","achv.spend-100.title":"挥金如土","achv.spend-100.desc":"Claude 累计花费 $100+","achv.tokens-100m.title":"Token 富翁","achv.tokens-100m.desc":"Codex 累计 100M tokens","achv.tokens-1b.title":"Token 大户","achv.tokens-1b.desc":"Codex 累计 1B tokens","achv.marathon-4h.title":"马拉松选手","achv.marathon-4h.desc":"单次会话超过 4 小时","achv.marathon-8h.title":"通宵达旦","achv.marathon-8h.desc":"单次会话超过 8 小时","achv.projects-10.title":"博学多才","achv.projects-10.desc":"在 10+ 个项目里工作过","achv.projects-30.title":"杂家","achv.projects-30.desc":"在 30+ 个项目里工作过","achv.night-owl.title":"夜猫子","achv.night-owl.desc":"过午夜的会话 10+ 次","achv.early-bird.title":"晨型人","achv.early-bird.desc":"早 7 点前的会话 5+ 次","achv.multi-tool.title":"多面手","achv.multi-tool.desc":"用过 3 种不同的工具","card.burndown.title":"近 7 天用量","card.burndown.header":"近 7 天用量 · {label}","card.burndown.notEnough":"数据不足以画图","card.burndown.tipUsed":"已用","card.burndown.legend5h":"5h","card.burndown.legend7d":"7d","card.leaderboard.title":"项目榜","card.leaderboard.hours":"小时","card.leaderboard.sessions":"会话","card.leaderboard.tools":"工具","card.leaderboard.noProjects":"还没有项目数据","card.hotspots.title":"文件热点","card.hotspots.sessions":"次会话","card.toolSplit.title":"工具分布","card.toolSplit.sessions":"次会话","card.toolSplit.totalSessions":"共 {n} 次会话","card.sessions.title":"会话","card.sessions.search":"搜项目 / 标题…","card.sessions.tag":"标签","card.sessions.tool":"工具","card.sessions.project":"项目 · 标题","card.sessions.duration":"时长","card.sessions.started":"开始","card.sessions.status":"状态","card.sessions.tags":"标签","card.sessions.recent":"最近会话","card.sessions.activeCount":"{active} 进行中 · {shown}/{total}","card.sessions.noMatch":"没有匹配的会话。","card.sessions.active":"进行中","card.sessions.done":"完成","card.sessions.tagPlaceholder":"标签…","admin.title":"Vibemeter · 管理","admin.subtitle":"本地 Codex 账户切换 · token 永不离开本机","admin.activeAccount":"当前 Codex 账户","admin.importCurrent":"保存当前 Codex 登录","admin.importing":"保存中…","admin.switch":"切换","admin.switching":"切换中…","admin.deleting":"删除中…","admin.savedAccounts":"已保存账户","admin.lastRefresh":"上次刷新","admin.activeAccountLabel":"当前 Codex 账户","admin.plan":"套餐","admin.accountId":"账户 id","admin.accessToken":"访问 token","admin.expires":"过期于 {date}","admin.noAuth":"未在 ~/.codex/auth.json 找到可读的 Codex 凭据。","admin.localStore":"本地账户存储","admin.localStoreHint":"快照存在 {path}。Token 落到磁盘是为了支持切换,本页只渲染解码后的元数据。","admin.colAccount":"账户","admin.colPlan":"套餐","admin.colTokens":"tokens","admin.colActions":"操作","admin.noSaved":"还没有已保存的账户。先登录 Codex,再来这里保存当前登录。","admin.active":"当前","admin.accessFmt":"访问 {date}","admin.savedFmt":"保存于 {date}","admin.importSuccess":"当前 Codex 登录已存入账户列表。","admin.switchSuccess":"Codex auth.json 已切换。重启正在运行的 Codex 会话以生效。","admin.deleteSuccess":"已删除该账户快照。","settings.title":"Vibemeter · 设置","settings.subtitle":"本地偏好 · 任何配置都不会离开本机","notify.title":"语音 + 系统通知","notify.subtitle":"仅 macOS。Claude/Codex 完成后,朗读「{tool} {project} 完成」并弹出系统通知。改动 ~/.claude/settings.json 与 ~/.codex/config.toml 前都会备份。","notify.on":"已开启","notify.off":"未开启","notify.claudeStop":"Claude · Stop","notify.claudeNotification":"Claude · Notification","notify.codexComplete":"Codex · complete","notify.foreignNotifyWarning":"检测到 Codex 已配置其他 notify 命令,Vibemeter 不会覆盖:","notify.foreignNotifyHint":"若想交给 Vibemeter 接管,请手动移除上面那行。","notify.enable":"开启语音通知","notify.reapply":"重新套用","notify.disable":"关闭","notify.scriptMissing":"语音脚本不存在:{path}。请重装 @hirra/vibemeter。","notify.hookStop":"Claude Code Stop hook","notify.hookNotification":"Claude Code Notification hook(需介入时)","notify.hookCodex":"Codex notify({state})","notify.hookCodexConfigFound":"已找到配置","notify.hookCodexConfigMissing":"未找到 ~/.codex/config.toml","notify.foreignWarn":"检测到 foreign notify","notify.foreignBody":"Codex 已经配置了其他 notify 命令,Vibemeter **不会**覆盖:","notify.foreignRemove":"如需 Vibemeter 接管,请手动移除上面那一行。","notify.installing":"开启中…","notify.removing":"关闭中…","notify.skipNoCodex":"Codex 配置未找到 — 已跳过","notify.skipForeign":"Codex 已有其他 notify 命令,已保留原值:{cmd}","notify.updated":"已更新。{notes}","notify.enabledMsg":"语音通知已开启。","notify.disabledMsg":"语音通知已关闭。","alerts.title":"额度推送提醒","alerts.subtitle":"quota 触发阈值、每日摘要、5h/weekly 重置前提醒 — 推到企业微信群机器人或任意 webhook。webhook 只写本地 {path},{warn}","alerts.subtitleWarn":"绝不会进 GitHub","alerts.sectionChannels":"推送通道","alerts.sectionRules":"触发规则","alerts.addChannelWxwork":"+ 企业微信","alerts.addChannelGeneric":"+ 通用 webhook","alerts.addRuleThreshold":"+ 阈值","alerts.addRuleDaily":"+ 每日定时","alerts.addRuleReset":"+ 重置提醒","alerts.noChannels":"尚未配置任何通道。","alerts.noRules":"尚未配置任何规则。","alerts.testBtn":"测试","alerts.testing":"测试中…","alerts.sendNow":"现在发摘要","alerts.sending":"发送中…","alerts.delete":"删除","alerts.webhookLabel":"Webhook URL","alerts.webhookHintWxwork":"— qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…","alerts.webhookPlaceholderEdit":"粘贴完整 webhook URL","alerts.webhookPlaceholderClick":"点击编辑","alerts.webhookStored":"已存储 — 编辑会替换;不编辑就保持原值","alerts.editFirst":"请先保存再测试","alerts.saveBtn":"保存配置","alerts.saving":"保存中…","alerts.runAllNow":"立即评估所有规则","alerts.evaluating":"评估中…","alerts.savedHint":"已保存。webhook 仅写入本地 ~/.vibemeter/alerts.json,不会上 GitHub。","alerts.testSuccess":"测试消息推送成功({channel}, {attempts} 次)","alerts.summarySuccess":"今日摘要推送成功({channel}, {attempts} 次)","alerts.pushFail":"推送失败:{message}","alerts.evalDone":"评估完成 — 触发 {fired} 次推送,启用规则 {evaluated} 条","alerts.kindThreshold":"阈值","alerts.kindDaily":"每日","alerts.kindReset":"重置提醒","alerts.enabled":"启用","alerts.metric":"指标","alerts.below":"低于","alerts.pushTime":"推送时间","alerts.pushTimeHint":"(本机时区,每天最多一条)","alerts.window":"窗口","alerts.minutesBefore":"提前","alerts.minutesBeforeHint":"分钟(60=1h,1440=1d)","alerts.remainingAbove":"剩余 ≥","alerts.remainingAboveHint":"% 时才推(赶在重置前用掉)","alerts.pushTo":"推送到","alerts.addChannelFirst":"先添加通道","alerts.pushLocale":"推送语言","alerts.pushLocaleHint":"群里发的消息用哪种语言(独立于 UI 语言)","alerts.pushLocaleZh":"中文","alerts.pushLocaleEn":"English","alerts.defaultLabelWxwork":"企业微信机器人","alerts.defaultLabelGeneric":"通用 webhook","alerts.testMsgWord":"测试消息","alerts.summaryWord":"今日摘要","alerts.pushFailUnknown":"未知错误","alerts.metric.claude_5h_remaining_pct":"Claude Code · 5h 剩余","alerts.metric.claude_weekly_remaining_pct":"Claude Code · 本周剩余","alerts.metric.codex_5h_remaining_pct":"Codex · 5h 剩余","alerts.metric.codex_weekly_remaining_pct":"Codex · 本周剩余","alerts.resetMetric.claude_5h":"Claude Code · 5h 窗口","alerts.resetMetric.claude_weekly":"Claude Code · 本周窗口","alerts.resetMetric.codex_5h":"Codex · 5h 窗口","alerts.resetMetric.codex_weekly":"Codex · 本周窗口","donate.title":"觉得好用?请作者喝杯咖啡","donate.subtitle":"Vibemeter 完全本地、完全免费。如果它帮你省了时间,欢迎随手打赏 — 无义务。","donate.alipay":"支付宝","donate.zoomClose":"关闭","donate.aside":"扫码即可,金额随意。备注里写一句你最常看哪个数据,下次迭代我会优先做。","donate.missing":"QR image not bundled yet — drop it at {path}.","float.unknownReset":"unknown reset","float.snapshotExpired":"snapshot expired","float.noSnapshot":"no snapshot","float.justNow":"just now","float.minAgo":"{n}m ago","float.hourAgo":"{n}h ago","float.currentQuota":"current quota","float.noQuota":"no quota","float.fiveHRemain":"5h remaining","float.usedPct":"{pct} used","float.usedNo":"No snapshot","float.refresh":"Refresh","float.refreshing":"Refreshing","float.statToday":"today","float.statTotal":"total","float.statWeekly":"weekly","float.noToday":"No sessions today","float.latest":"latest"},en:{"common.dashboard":"Dashboard","common.admin":"Admin","common.settings":"Settings","common.cancel":"Cancel","common.save":"Save","common.delete":"Delete","common.close":"Close","common.add":"Add","common.edit":"Edit","common.refresh":"Refresh","common.loading":"Loading…","common.unknown":"unknown","common.all":"All","common.empty":"no data yet","common.language":"Language","header.tagline":"measure your AI coding vibe · local-first · data never leaves this machine","header.langZh":"中文","header.langEn":"EN","dashboard.refreshData":"Refresh data","dashboard.refreshing":"Refreshing…","dashboard.toolAll":"All","dashboard.toolClaude":"Claude Code","dashboard.toolCodex":"Codex","dashboard.toolCursor":"Cursor","dashboard.dateToday":"Today","dashboard.date7d":"7d","dashboard.date30d":"30d","dashboard.dateAll":"All","dashboard.codexAccount":"Codex account","dashboard.codexAccountAll":"All Codex accounts","dashboard.codexHistoryFilter.current":"current account only","dashboard.codexHistoryFilter.saved":"saved account history only","dashboard.codexHistoryFilter.all":"all saved and legacy Codex usage","dashboard.codexSwitchHint":"Dashboard selection filters saved history only. Switch this account in Admin to collect new Codex usage.","dashboard.window5h":"5h window","dashboard.windowWeekly":"weekly window","dashboard.remaining":"remaining","dashboard.used":"used","dashboard.noData":"no data yet","dashboard.todaySessions":"Today's sessions","dashboard.totalSessions":"Total sessions","dashboard.dateToday2":"Today","dashboard.date7days":"7 days","dashboard.date30days":"30 days","dashboard.dateAlltime":"All time","dashboard.refreshFailed":"Refresh failed","dashboard.scanned":"scanned {n} logs in {seconds}s","dashboard.codexCurrentOnly":"current account only","dashboard.codexSavedOnly":"saved account history only","dashboard.codexAllUsage":"all saved and legacy Codex usage","dashboard.window7d":"7-day budget","dashboard.codexLabel":"codex · all accounts","dashboard.codexLabelPrefix":"codex","dashboard.allAgents":"all agents","dashboard.claudeCodeLower":"claude code","dashboard.noSnapshot":"no rate-limit snapshot for this account yet","dashboard.useCodexThenRefresh":"use Codex once, then Refresh data","dashboard.switchAdminThenUse":"switch it in Admin, use Codex once, then Refresh data","dashboard.resetting":"resetting…","dashboard.resetsAt":"resets at {date} (in {rel})","dashboard.short":"all","card.spending.title":"spending / consumption","card.spending.claudeCost":"claude code total","card.spending.codexTokens":"codex tokens","card.spending.last14d":"last 14 days","card.spending.noCursor":"no spending data for cursor","card.spending.legendClaude":"claude ($)","card.spending.legendCodex":"codex (tokens)","card.activity.title":"activity","card.activity.streakDays":"{n}-day streak","card.activity.peakSlot":"peak slot","card.activity.todayTimeline":"today's timeline","card.activity.viewPattern":"pattern","card.activity.viewToday":"today","card.activity.dayStreak":"day streak","card.activity.longest":"longest","card.activity.activeDays":"active days","card.activity.total":"total","card.activity.peakSlotFmt":"peak slot: {day} {hour}:00 ({mins})","card.activity.dayMon":"Mon","card.activity.dayTue":"Tue","card.activity.dayWed":"Wed","card.activity.dayThu":"Thu","card.activity.dayFri":"Fri","card.activity.daySat":"Sat","card.activity.daySun":"Sun","card.activity.less":"less","card.activity.more":"more","card.activity.dayHeader":"{date} · {n} sessions · {hours}h active","card.activity.noToday":"no sessions today","card.achievements.title":"achievements","card.achievements.unlocked":"{n} / {total} unlocked","card.achievements.showLess":"show less","card.achievements.showAll":"show all ({n})","achv.sessions-100.title":"Centurion","achv.sessions-100.desc":"100 sessions logged","achv.sessions-500.title":"Habituated","achv.sessions-500.desc":"500 sessions logged","achv.sessions-1000.title":"Veteran","achv.sessions-1000.desc":"1000 sessions logged","achv.streak-7.title":"Week streak","achv.streak-7.desc":"7-day coding streak","achv.streak-30.title":"Month streak","achv.streak-30.desc":"30-day coding streak","achv.spend-10.title":"First $10","achv.spend-10.desc":"Claude spend $10+","achv.spend-100.title":"Big spender","achv.spend-100.desc":"Claude spend $100+","achv.tokens-100m.title":"Token rich","achv.tokens-100m.desc":"100M Codex tokens","achv.tokens-1b.title":"Token tycoon","achv.tokens-1b.desc":"1B Codex tokens","achv.marathon-4h.title":"Marathoner","achv.marathon-4h.desc":"Single session 4h+","achv.marathon-8h.title":"All-nighter","achv.marathon-8h.desc":"Single session 8h+","achv.projects-10.title":"Polymath","achv.projects-10.desc":"Worked on 10+ projects","achv.projects-30.title":"Jack of all","achv.projects-30.desc":"Worked on 30+ projects","achv.night-owl.title":"Night owl","achv.night-owl.desc":"Past-midnight sessions 10+","achv.early-bird.title":"Early bird","achv.early-bird.desc":"Pre-7am sessions 5+","achv.multi-tool.title":"Multi-tool","achv.multi-tool.desc":"Used 3 different tools","card.burndown.title":"7-day burndown","card.burndown.header":"usage history (7d) · {label}","card.burndown.notEnough":"not enough data yet","card.burndown.tipUsed":"used","card.burndown.legend5h":"5h","card.burndown.legend7d":"7day","card.leaderboard.title":"project leaderboard","card.leaderboard.hours":"hours","card.leaderboard.sessions":"sessions","card.leaderboard.tools":"tools","card.leaderboard.noProjects":"no projects yet","card.hotspots.title":"file hotspots","card.hotspots.sessions":"sessions","card.toolSplit.title":"tool split","card.toolSplit.sessions":"sessions","card.toolSplit.totalSessions":"{n} total sessions","card.sessions.title":"Sessions","card.sessions.search":"search project / title…","card.sessions.tag":"tag","card.sessions.tool":"tool","card.sessions.project":"project · title","card.sessions.duration":"dur","card.sessions.started":"started","card.sessions.status":"status","card.sessions.tags":"tags","card.sessions.recent":"Recent Sessions","card.sessions.activeCount":"{active} active · {shown}/{total}","card.sessions.noMatch":"No sessions match.","card.sessions.active":"active","card.sessions.done":"done","card.sessions.tagPlaceholder":"tag…","admin.title":"Vibemeter Admin","admin.subtitle":"local Codex account switching · tokens stay on this machine","admin.activeAccount":"Active Codex account","admin.importCurrent":"Save current Codex login","admin.importing":"Saving…","admin.switch":"Switch","admin.switching":"Switching…","admin.deleting":"Deleting…","admin.savedAccounts":"Saved accounts","admin.lastRefresh":"last refresh","admin.activeAccountLabel":"Active Codex account","admin.plan":"plan","admin.accountId":"account id","admin.accessToken":"access token","admin.expires":"expires {date}","admin.noAuth":"No readable Codex auth found at ~/.codex/auth.json.","admin.localStore":"Local account store","admin.localStoreHint":"Saved snapshots live in {path}. Tokens are stored on disk for switching, but this page only renders decoded metadata.","admin.colAccount":"account","admin.colPlan":"plan","admin.colTokens":"tokens","admin.colActions":"actions","admin.noSaved":"No saved accounts yet. Log into Codex, then save the current login here.","admin.active":"active","admin.accessFmt":"access {date}","admin.savedFmt":"saved {date}","admin.importSuccess":"Current Codex login saved to the account list.","admin.switchSuccess":"Codex auth.json switched. Restart active Codex sessions to use the new account.","admin.deleteSuccess":"Saved account removed.","settings.title":"Vibemeter Settings","settings.subtitle":"local preferences · nothing leaves this machine","notify.title":"Voice + system notifications","notify.subtitle":'macOS only. When an agent finishes you\'ll hear a short "{tool} {project} done" and see a banner. ~/.claude/settings.json and ~/.codex/config.toml are backed up before any change.',"notify.on":"on","notify.off":"off","notify.claudeStop":"Claude · Stop","notify.claudeNotification":"Claude · Notification","notify.codexComplete":"Codex · complete","notify.foreignNotifyWarning":"Codex already has a different notify command. Vibemeter will not overwrite it:","notify.foreignNotifyHint":"Remove that line manually if you want Vibemeter to take over.","notify.enable":"Enable voice notifications","notify.reapply":"Re-apply","notify.disable":"Disable","notify.scriptMissing":"Speaker script not found at {path}. Reinstall @hirra/vibemeter.","notify.hookStop":"Claude Code Stop hook","notify.hookNotification":"Claude Code Notification hook (when permission needed)","notify.hookCodex":"Codex notify ({state})","notify.hookCodexConfigFound":"config found","notify.hookCodexConfigMissing":"no ~/.codex/config.toml","notify.foreignWarn":"foreign notify exists","notify.foreignBody":"Codex already has a different notify command. Vibemeter will **not** overwrite it:","notify.foreignRemove":"Remove that line manually if you want Vibemeter to take over.","notify.installing":"Enabling…","notify.removing":"Removing…","notify.skipNoCodex":"Codex config not found — skipped","notify.skipForeign":"Codex notify already set to a different command, left alone: {cmd}","notify.updated":"Updated. {notes}","notify.enabledMsg":"Voice notifications enabled.","notify.disabledMsg":"Voice notifications removed.","alerts.title":"Quota push alerts","alerts.subtitle":"threshold trips, daily summary, 5h/weekly reset reminders — push to WeChat Work bot or any webhook. Webhook is written to {path} only — {warn}","alerts.subtitleWarn":"never reaches GitHub","alerts.sectionChannels":"Push channels","alerts.sectionRules":"Trigger rules","alerts.addChannelWxwork":"+ WeChat Work","alerts.addChannelGeneric":"+ Generic webhook","alerts.addRuleThreshold":"+ Threshold","alerts.addRuleDaily":"+ Daily","alerts.addRuleReset":"+ Reset reminder","alerts.noChannels":"No channels yet.","alerts.noRules":"No rules yet.","alerts.testBtn":"Test","alerts.testing":"Testing…","alerts.sendNow":"Send summary now","alerts.sending":"Sending…","alerts.delete":"Delete","alerts.webhookLabel":"Webhook URL","alerts.webhookHintWxwork":"— qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…","alerts.webhookPlaceholderEdit":"Paste the full webhook URL","alerts.webhookPlaceholderClick":"Click to edit","alerts.webhookStored":"Stored — editing replaces; leave alone to keep current.","alerts.editFirst":"Save first before testing","alerts.saveBtn":"Save","alerts.saving":"Saving…","alerts.runAllNow":"Evaluate all rules now","alerts.evaluating":"Evaluating…","alerts.savedHint":"Saved. Webhook is only written to ~/.vibemeter/alerts.json — never uploaded to GitHub.","alerts.testSuccess":"Test message delivered ({channel}, {attempts} attempt(s))","alerts.summarySuccess":"Today's summary delivered ({channel}, {attempts} attempt(s))","alerts.pushFail":"Push failed: {message}","alerts.evalDone":"Done — {fired} push(es) fired, {evaluated} enabled rule(s)","alerts.kindThreshold":"threshold","alerts.kindDaily":"daily","alerts.kindReset":"reset","alerts.enabled":"enabled","alerts.metric":"Metric","alerts.below":"Below","alerts.pushTime":"Push at","alerts.pushTimeHint":"(local time, at most one per day)","alerts.window":"Window","alerts.minutesBefore":"Lead","alerts.minutesBeforeHint":"min (60=1h, 1440=1d)","alerts.remainingAbove":"Remaining ≥","alerts.remainingAboveHint":"% (only nudges while quota is worth saving)","alerts.pushTo":"Push to","alerts.addChannelFirst":"Add a channel first","alerts.pushLocale":"Push language","alerts.pushLocaleHint":"Language used in pushed messages (independent of UI language)","alerts.pushLocaleZh":"中文","alerts.pushLocaleEn":"English","alerts.defaultLabelWxwork":"WeChat Work bot","alerts.defaultLabelGeneric":"Generic webhook","alerts.testMsgWord":"Test message","alerts.summaryWord":"Today's summary","alerts.pushFailUnknown":"unknown error","alerts.metric.claude_5h_remaining_pct":"Claude Code · 5h remaining","alerts.metric.claude_weekly_remaining_pct":"Claude Code · weekly remaining","alerts.metric.codex_5h_remaining_pct":"Codex · 5h remaining","alerts.metric.codex_weekly_remaining_pct":"Codex · weekly remaining","alerts.resetMetric.claude_5h":"Claude Code · 5h window","alerts.resetMetric.claude_weekly":"Claude Code · weekly window","alerts.resetMetric.codex_5h":"Codex · 5h window","alerts.resetMetric.codex_weekly":"Codex · weekly window","donate.title":"Tip the author","donate.subtitle":"Vibemeter is fully local and free. If it saves you time, a small tip keeps the project alive — no obligation.","donate.alipay":"Alipay","donate.zoomClose":"Close","donate.aside":"Scan and pay any amount. Drop a note about the dashboard view you use the most — that drives the next iteration.","donate.missing":"QR image not bundled yet — drop it at {path}.","float.unknownReset":"未知重置时间","float.snapshotExpired":"快照已过期","float.noSnapshot":"尚无快照","float.justNow":"刚刚","float.minAgo":"{n} 分钟前","float.hourAgo":"{n} 小时前","float.currentQuota":"当前额度","float.noQuota":"无额度","float.fiveHRemain":"5h 剩余","float.usedPct":"已用 {pct}","float.usedNo":"尚无数据","float.refresh":"刷新","float.refreshing":"刷新中","float.statToday":"今日","float.statTotal":"累计","float.statWeekly":"本周","float.noToday":"今天还没有会话","float.latest":"最近"}},e=(0,c.createContext)("zh");function f(){return(0,c.useContext)(e)}a.s(["LocaleProvider",0,function({locale:a,children:c}){return(0,b.jsx)(e.Provider,{value:a,children:c})},"useLocale",0,f,"useT",0,function(){let a=f();return(0,c.useMemo)(()=>(b,c)=>{var e,f,g;let h;return e=a,f=b,g=c,h=(d[e]??d.zh)[f]??f,g?h.replace(/\{(\w+)\}/g,(a,b)=>{let c=g[b];return null==c?`{${b}}`:String(c)}):h},[a])}],42553)}];
|
|
2
|
+
|
|
3
|
+
//# sourceMappingURL=src_lib_i18n_client_tsx_07pysgz._.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/lib/i18n/client.tsx","../../../../src/lib/i18n/types.ts","../../../../src/lib/i18n/messages.ts","../../../../src/lib/i18n/index.ts"],"sourcesContent":["'use client';\n\n// Client-side locale plumbing. A provider receives the locale from the root\n// server layout (one source of truth, cookie-derived); descendants read it\n// via useLocale() and call useT() to translate without prop-drilling.\n\nimport { createContext, useContext, useMemo } from 'react';\nimport { DEFAULT_LOCALE, type Locale } from './types';\nimport { t as translate } from './index';\n\nconst LocaleContext = createContext<Locale>(DEFAULT_LOCALE);\n\nexport function LocaleProvider({ locale, children }: { locale: Locale; children: React.ReactNode }) {\n return <LocaleContext.Provider value={locale}>{children}</LocaleContext.Provider>;\n}\n\nexport function useLocale(): Locale {\n return useContext(LocaleContext);\n}\n\nexport function useT(): (key: string, vars?: Record<string, string | number>) => string {\n const locale = useLocale();\n return useMemo(() => (key: string, vars?: Record<string, string | number>) => translate(locale, key, vars), [locale]);\n}\n","// Locale types + constants. Default Chinese — toggle via cookie + header switcher.\n\nexport type Locale = 'zh' | 'en';\nexport const LOCALES: readonly Locale[] = ['zh', 'en'] as const;\nexport const DEFAULT_LOCALE: Locale = 'zh';\nexport const LOCALE_COOKIE = 'vibemeter_locale';\n\nexport function isLocale(v: unknown): v is Locale {\n return v === 'zh' || v === 'en';\n}\n\nexport function normalizeLocale(v: unknown): Locale {\n return isLocale(v) ? v : DEFAULT_LOCALE;\n}\n","// All user-facing strings. Keep keys hierarchical so it's clear which screen\n// owns which copy. Translations are full sentences (not word-by-word) so the\n// other locale can flow naturally.\n\nimport type { Locale } from './types';\n\nexport type Messages = Record<string, string>;\n\nconst zh: Messages = {\n // ── common ────────────────────────────────────────────────────────────\n 'common.dashboard': '仪表盘',\n 'common.admin': '管理',\n 'common.settings': '设置',\n 'common.cancel': '取消',\n 'common.save': '保存',\n 'common.delete': '删除',\n 'common.close': '关闭',\n 'common.add': '添加',\n 'common.edit': '编辑',\n 'common.refresh': '刷新',\n 'common.loading': '加载中…',\n 'common.unknown': '未知',\n 'common.all': '全部',\n 'common.empty': '暂无数据',\n 'common.language': '语言',\n\n // ── header / layout ───────────────────────────────────────────────────\n 'header.tagline': '量化你的 AI 编码节奏 · 本地优先 · 数据永不离开本机',\n 'header.langZh': '中文',\n 'header.langEn': 'EN',\n\n // ── dashboard / filters ───────────────────────────────────────────────\n 'dashboard.refreshData': '刷新数据',\n 'dashboard.refreshing': '刷新中…',\n 'dashboard.toolAll': '全部',\n 'dashboard.toolClaude': 'Claude Code',\n 'dashboard.toolCodex': 'Codex',\n 'dashboard.toolCursor': 'Cursor',\n 'dashboard.dateToday': '今天',\n 'dashboard.date7d': '近 7 天',\n 'dashboard.date30d': '近 30 天',\n 'dashboard.dateAll': '全部',\n 'dashboard.codexAccount': 'Codex 账户',\n 'dashboard.codexAccountAll': '所有 Codex 账户',\n 'dashboard.codexHistoryFilter.current': '仅当前账户',\n 'dashboard.codexHistoryFilter.saved': '仅已保存账户的历史',\n 'dashboard.codexHistoryFilter.all': '所有已保存与历史 Codex 使用',\n 'dashboard.codexSwitchHint': '当前选项只筛选历史。如要采集新数据,请到「管理」切换账户。',\n 'dashboard.window5h': '5h 窗口',\n 'dashboard.windowWeekly': '本周窗口',\n 'dashboard.remaining': '剩余',\n 'dashboard.used': '已用',\n 'dashboard.noData': '暂无数据',\n 'dashboard.todaySessions': '今日会话',\n 'dashboard.totalSessions': '累计会话',\n 'dashboard.dateToday2': '今天',\n 'dashboard.date7days': '近 7 天',\n 'dashboard.date30days': '近 30 天',\n 'dashboard.dateAlltime': '全部时间',\n 'dashboard.refreshFailed': '刷新失败',\n 'dashboard.scanned': '扫了 {n} 条日志,耗时 {seconds}s',\n 'dashboard.codexCurrentOnly': '仅当前账户',\n 'dashboard.codexSavedOnly': '仅已保存账户的历史',\n 'dashboard.codexAllUsage': '所有已保存与历史 Codex 使用',\n 'dashboard.window7d': '7 天预算',\n 'dashboard.codexLabel': 'codex · 所有账户',\n 'dashboard.codexLabelPrefix': 'codex',\n 'dashboard.allAgents': '所有 agents',\n 'dashboard.claudeCodeLower': 'claude code',\n 'dashboard.noSnapshot': '该账户暂无 rate-limit 快照',\n 'dashboard.useCodexThenRefresh': '在 Codex 用一次再刷新',\n 'dashboard.switchAdminThenUse': '到「管理」切换后用一次 Codex,再刷新',\n 'dashboard.resetting': '重置中…',\n 'dashboard.resetsAt': '{date}({rel}后)重置',\n 'dashboard.short': 'all',\n\n // ── cards ─────────────────────────────────────────────────────────────\n 'card.spending.title': '消费 / 用量',\n 'card.spending.claudeCost': 'claude code 累计',\n 'card.spending.codexTokens': 'codex tokens',\n 'card.spending.last14d': '近 14 天',\n 'card.spending.noCursor': '当前筛选下无消费数据(cursor 无 token 计费)',\n 'card.spending.legendClaude': 'claude ($)',\n 'card.spending.legendCodex': 'codex (tokens)',\n 'card.activity.title': '活跃度',\n 'card.activity.streakDays': '连续 {n} 天',\n 'card.activity.peakSlot': '高峰时段',\n 'card.activity.todayTimeline': '今日时间线',\n 'card.activity.viewPattern': '热力图',\n 'card.activity.viewToday': '今日',\n 'card.activity.dayStreak': '天连续',\n 'card.activity.longest': '最长',\n 'card.activity.activeDays': '活跃天',\n 'card.activity.total': '累计',\n 'card.activity.peakSlotFmt': '高峰时段:{day} {hour}:00({mins})',\n 'card.activity.dayMon': '一',\n 'card.activity.dayTue': '二',\n 'card.activity.dayWed': '三',\n 'card.activity.dayThu': '四',\n 'card.activity.dayFri': '五',\n 'card.activity.daySat': '六',\n 'card.activity.daySun': '日',\n 'card.activity.less': '少',\n 'card.activity.more': '多',\n 'card.activity.dayHeader': '{date} · {n} 次会话 · 累计 {hours}h',\n 'card.activity.noToday': '今天还没有会话',\n 'card.achievements.title': '成就',\n 'card.achievements.unlocked': '已解锁 {n} / {total}',\n 'card.achievements.showLess': '收起',\n 'card.achievements.showAll': '查看全部({n})',\n 'achv.sessions-100.title': '百战不殆', 'achv.sessions-100.desc': '累计 100 个会话',\n 'achv.sessions-500.title': '已成习惯', 'achv.sessions-500.desc': '累计 500 个会话',\n 'achv.sessions-1000.title': '千锤百炼', 'achv.sessions-1000.desc': '累计 1000 个会话',\n 'achv.streak-7.title': '一周连击', 'achv.streak-7.desc': '连续编码 7 天',\n 'achv.streak-30.title': '月度坚持', 'achv.streak-30.desc': '连续编码 30 天',\n 'achv.spend-10.title': '小试牛刀', 'achv.spend-10.desc': 'Claude 累计花费 $10+',\n 'achv.spend-100.title': '挥金如土', 'achv.spend-100.desc': 'Claude 累计花费 $100+',\n 'achv.tokens-100m.title': 'Token 富翁', 'achv.tokens-100m.desc': 'Codex 累计 100M tokens',\n 'achv.tokens-1b.title': 'Token 大户', 'achv.tokens-1b.desc': 'Codex 累计 1B tokens',\n 'achv.marathon-4h.title': '马拉松选手', 'achv.marathon-4h.desc': '单次会话超过 4 小时',\n 'achv.marathon-8h.title': '通宵达旦', 'achv.marathon-8h.desc': '单次会话超过 8 小时',\n 'achv.projects-10.title': '博学多才', 'achv.projects-10.desc': '在 10+ 个项目里工作过',\n 'achv.projects-30.title': '杂家', 'achv.projects-30.desc': '在 30+ 个项目里工作过',\n 'achv.night-owl.title': '夜猫子', 'achv.night-owl.desc': '过午夜的会话 10+ 次',\n 'achv.early-bird.title': '晨型人', 'achv.early-bird.desc': '早 7 点前的会话 5+ 次',\n 'achv.multi-tool.title': '多面手', 'achv.multi-tool.desc': '用过 3 种不同的工具',\n 'card.burndown.title': '近 7 天用量',\n 'card.burndown.header': '近 7 天用量 · {label}',\n 'card.burndown.notEnough': '数据不足以画图',\n 'card.burndown.tipUsed': '已用',\n 'card.burndown.legend5h': '5h',\n 'card.burndown.legend7d': '7d',\n 'card.leaderboard.title': '项目榜',\n 'card.leaderboard.hours': '小时',\n 'card.leaderboard.sessions': '会话',\n 'card.leaderboard.tools': '工具',\n 'card.leaderboard.noProjects': '还没有项目数据',\n 'card.hotspots.title': '文件热点',\n 'card.hotspots.sessions': '次会话',\n 'card.toolSplit.title': '工具分布',\n 'card.toolSplit.sessions': '次会话',\n 'card.toolSplit.totalSessions': '共 {n} 次会话',\n 'card.sessions.title': '会话',\n 'card.sessions.search': '搜项目 / 标题…',\n 'card.sessions.tag': '标签',\n 'card.sessions.tool': '工具',\n 'card.sessions.project': '项目 · 标题',\n 'card.sessions.duration': '时长',\n 'card.sessions.started': '开始',\n 'card.sessions.status': '状态',\n 'card.sessions.tags': '标签',\n 'card.sessions.recent': '最近会话',\n 'card.sessions.activeCount': '{active} 进行中 · {shown}/{total}',\n 'card.sessions.noMatch': '没有匹配的会话。',\n 'card.sessions.active': '进行中',\n 'card.sessions.done': '完成',\n 'card.sessions.tagPlaceholder': '标签…',\n\n // ── admin ─────────────────────────────────────────────────────────────\n 'admin.title': 'Vibemeter · 管理',\n 'admin.subtitle': '本地 Codex 账户切换 · token 永不离开本机',\n 'admin.activeAccount': '当前 Codex 账户',\n 'admin.importCurrent': '保存当前 Codex 登录',\n 'admin.importing': '保存中…',\n 'admin.switch': '切换',\n 'admin.switching': '切换中…',\n 'admin.deleting': '删除中…',\n 'admin.savedAccounts': '已保存账户',\n 'admin.lastRefresh': '上次刷新',\n 'admin.activeAccountLabel': '当前 Codex 账户',\n 'admin.plan': '套餐',\n 'admin.accountId': '账户 id',\n 'admin.accessToken': '访问 token',\n 'admin.expires': '过期于 {date}',\n 'admin.noAuth': '未在 ~/.codex/auth.json 找到可读的 Codex 凭据。',\n 'admin.localStore': '本地账户存储',\n 'admin.localStoreHint': '快照存在 {path}。Token 落到磁盘是为了支持切换,本页只渲染解码后的元数据。',\n 'admin.colAccount': '账户',\n 'admin.colPlan': '套餐',\n 'admin.colTokens': 'tokens',\n 'admin.colActions': '操作',\n 'admin.noSaved': '还没有已保存的账户。先登录 Codex,再来这里保存当前登录。',\n 'admin.active': '当前',\n 'admin.accessFmt': '访问 {date}',\n 'admin.savedFmt': '保存于 {date}',\n 'admin.importSuccess': '当前 Codex 登录已存入账户列表。',\n 'admin.switchSuccess': 'Codex auth.json 已切换。重启正在运行的 Codex 会话以生效。',\n 'admin.deleteSuccess': '已删除该账户快照。',\n\n // ── settings page ─────────────────────────────────────────────────────\n 'settings.title': 'Vibemeter · 设置',\n 'settings.subtitle': '本地偏好 · 任何配置都不会离开本机',\n\n // ── settings · notify ─────────────────────────────────────────────────\n 'notify.title': '语音 + 系统通知',\n 'notify.subtitle': '仅 macOS。Claude/Codex 完成后,朗读「{tool} {project} 完成」并弹出系统通知。改动 ~/.claude/settings.json 与 ~/.codex/config.toml 前都会备份。',\n 'notify.on': '已开启',\n 'notify.off': '未开启',\n 'notify.claudeStop': 'Claude · Stop',\n 'notify.claudeNotification': 'Claude · Notification',\n 'notify.codexComplete': 'Codex · complete',\n 'notify.foreignNotifyWarning': '检测到 Codex 已配置其他 notify 命令,Vibemeter 不会覆盖:',\n 'notify.foreignNotifyHint': '若想交给 Vibemeter 接管,请手动移除上面那行。',\n 'notify.enable': '开启语音通知',\n 'notify.reapply': '重新套用',\n 'notify.disable': '关闭',\n 'notify.scriptMissing': '语音脚本不存在:{path}。请重装 @hirra/vibemeter。',\n 'notify.hookStop': 'Claude Code Stop hook',\n 'notify.hookNotification': 'Claude Code Notification hook(需介入时)',\n 'notify.hookCodex': 'Codex notify({state})',\n 'notify.hookCodexConfigFound': '已找到配置',\n 'notify.hookCodexConfigMissing': '未找到 ~/.codex/config.toml',\n 'notify.foreignWarn': '检测到 foreign notify',\n 'notify.foreignBody': 'Codex 已经配置了其他 notify 命令,Vibemeter **不会**覆盖:',\n 'notify.foreignRemove': '如需 Vibemeter 接管,请手动移除上面那一行。',\n 'notify.installing': '开启中…',\n 'notify.removing': '关闭中…',\n 'notify.skipNoCodex': 'Codex 配置未找到 — 已跳过',\n 'notify.skipForeign': 'Codex 已有其他 notify 命令,已保留原值:{cmd}',\n 'notify.updated': '已更新。{notes}',\n 'notify.enabledMsg': '语音通知已开启。',\n 'notify.disabledMsg': '语音通知已关闭。',\n\n // ── settings · alerts ─────────────────────────────────────────────────\n 'alerts.title': '额度推送提醒',\n 'alerts.subtitle': 'quota 触发阈值、每日摘要、5h/weekly 重置前提醒 — 推到企业微信群机器人或任意 webhook。webhook 只写本地 {path},{warn}',\n 'alerts.subtitleWarn': '绝不会进 GitHub',\n 'alerts.sectionChannels': '推送通道',\n 'alerts.sectionRules': '触发规则',\n 'alerts.addChannelWxwork': '+ 企业微信',\n 'alerts.addChannelGeneric': '+ 通用 webhook',\n 'alerts.addRuleThreshold': '+ 阈值',\n 'alerts.addRuleDaily': '+ 每日定时',\n 'alerts.addRuleReset': '+ 重置提醒',\n 'alerts.noChannels': '尚未配置任何通道。',\n 'alerts.noRules': '尚未配置任何规则。',\n 'alerts.testBtn': '测试',\n 'alerts.testing': '测试中…',\n 'alerts.sendNow': '现在发摘要',\n 'alerts.sending': '发送中…',\n 'alerts.delete': '删除',\n 'alerts.webhookLabel': 'Webhook URL',\n 'alerts.webhookHintWxwork': '— qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…',\n 'alerts.webhookPlaceholderEdit': '粘贴完整 webhook URL',\n 'alerts.webhookPlaceholderClick': '点击编辑',\n 'alerts.webhookStored': '已存储 — 编辑会替换;不编辑就保持原值',\n 'alerts.editFirst': '请先保存再测试',\n 'alerts.saveBtn': '保存配置',\n 'alerts.saving': '保存中…',\n 'alerts.runAllNow': '立即评估所有规则',\n 'alerts.evaluating': '评估中…',\n 'alerts.savedHint': '已保存。webhook 仅写入本地 ~/.vibemeter/alerts.json,不会上 GitHub。',\n 'alerts.testSuccess': '测试消息推送成功({channel}, {attempts} 次)',\n 'alerts.summarySuccess': '今日摘要推送成功({channel}, {attempts} 次)',\n 'alerts.pushFail': '推送失败:{message}',\n 'alerts.evalDone': '评估完成 — 触发 {fired} 次推送,启用规则 {evaluated} 条',\n 'alerts.kindThreshold': '阈值',\n 'alerts.kindDaily': '每日',\n 'alerts.kindReset': '重置提醒',\n 'alerts.enabled': '启用',\n 'alerts.metric': '指标',\n 'alerts.below': '低于',\n 'alerts.pushTime': '推送时间',\n 'alerts.pushTimeHint': '(本机时区,每天最多一条)',\n 'alerts.window': '窗口',\n 'alerts.minutesBefore': '提前',\n 'alerts.minutesBeforeHint': '分钟(60=1h,1440=1d)',\n 'alerts.remainingAbove': '剩余 ≥',\n 'alerts.remainingAboveHint': '% 时才推(赶在重置前用掉)',\n 'alerts.pushTo': '推送到',\n 'alerts.addChannelFirst': '先添加通道',\n 'alerts.pushLocale': '推送语言',\n 'alerts.pushLocaleHint': '群里发的消息用哪种语言(独立于 UI 语言)',\n 'alerts.pushLocaleZh': '中文',\n 'alerts.pushLocaleEn': 'English',\n 'alerts.defaultLabelWxwork': '企业微信机器人',\n 'alerts.defaultLabelGeneric': '通用 webhook',\n 'alerts.testMsgWord': '测试消息',\n 'alerts.summaryWord': '今日摘要',\n 'alerts.pushFailUnknown': '未知错误',\n 'alerts.metric.claude_5h_remaining_pct': 'Claude Code · 5h 剩余',\n 'alerts.metric.claude_weekly_remaining_pct': 'Claude Code · 本周剩余',\n 'alerts.metric.codex_5h_remaining_pct': 'Codex · 5h 剩余',\n 'alerts.metric.codex_weekly_remaining_pct': 'Codex · 本周剩余',\n 'alerts.resetMetric.claude_5h': 'Claude Code · 5h 窗口',\n 'alerts.resetMetric.claude_weekly': 'Claude Code · 本周窗口',\n 'alerts.resetMetric.codex_5h': 'Codex · 5h 窗口',\n 'alerts.resetMetric.codex_weekly': 'Codex · 本周窗口',\n\n // ── settings · donate ─────────────────────────────────────────────────\n 'donate.title': '觉得好用?请作者喝杯咖啡',\n 'donate.subtitle': 'Vibemeter 完全本地、完全免费。如果它帮你省了时间,欢迎随手打赏 — 无义务。',\n 'donate.alipay': '支付宝',\n 'donate.zoomClose': '关闭',\n 'donate.aside': '扫码即可,金额随意。备注里写一句你最常看哪个数据,下次迭代我会优先做。',\n 'donate.missing': 'QR image not bundled yet — drop it at {path}.',\n\n // ── float widget ──────────────────────────────────────────────────────\n 'float.unknownReset': 'unknown reset',\n 'float.snapshotExpired': 'snapshot expired',\n 'float.noSnapshot': 'no snapshot',\n 'float.justNow': 'just now',\n 'float.minAgo': '{n}m ago',\n 'float.hourAgo': '{n}h ago',\n 'float.currentQuota': 'current quota',\n 'float.noQuota': 'no quota',\n 'float.fiveHRemain': '5h remaining',\n 'float.usedPct': '{pct} used',\n 'float.usedNo': 'No snapshot',\n 'float.refresh': 'Refresh',\n 'float.refreshing': 'Refreshing',\n 'float.statToday': 'today',\n 'float.statTotal': 'total',\n 'float.statWeekly': 'weekly',\n 'float.noToday': 'No sessions today',\n 'float.latest': 'latest',\n};\n\nconst en: Messages = {\n // ── common ────────────────────────────────────────────────────────────\n 'common.dashboard': 'Dashboard',\n 'common.admin': 'Admin',\n 'common.settings': 'Settings',\n 'common.cancel': 'Cancel',\n 'common.save': 'Save',\n 'common.delete': 'Delete',\n 'common.close': 'Close',\n 'common.add': 'Add',\n 'common.edit': 'Edit',\n 'common.refresh': 'Refresh',\n 'common.loading': 'Loading…',\n 'common.unknown': 'unknown',\n 'common.all': 'All',\n 'common.empty': 'no data yet',\n 'common.language': 'Language',\n\n // ── header / layout ───────────────────────────────────────────────────\n 'header.tagline': 'measure your AI coding vibe · local-first · data never leaves this machine',\n 'header.langZh': '中文',\n 'header.langEn': 'EN',\n\n // ── dashboard / filters ───────────────────────────────────────────────\n 'dashboard.refreshData': 'Refresh data',\n 'dashboard.refreshing': 'Refreshing…',\n 'dashboard.toolAll': 'All',\n 'dashboard.toolClaude': 'Claude Code',\n 'dashboard.toolCodex': 'Codex',\n 'dashboard.toolCursor': 'Cursor',\n 'dashboard.dateToday': 'Today',\n 'dashboard.date7d': '7d',\n 'dashboard.date30d': '30d',\n 'dashboard.dateAll': 'All',\n 'dashboard.codexAccount': 'Codex account',\n 'dashboard.codexAccountAll': 'All Codex accounts',\n 'dashboard.codexHistoryFilter.current': 'current account only',\n 'dashboard.codexHistoryFilter.saved': 'saved account history only',\n 'dashboard.codexHistoryFilter.all': 'all saved and legacy Codex usage',\n 'dashboard.codexSwitchHint': 'Dashboard selection filters saved history only. Switch this account in Admin to collect new Codex usage.',\n 'dashboard.window5h': '5h window',\n 'dashboard.windowWeekly': 'weekly window',\n 'dashboard.remaining': 'remaining',\n 'dashboard.used': 'used',\n 'dashboard.noData': 'no data yet',\n 'dashboard.todaySessions': \"Today's sessions\",\n 'dashboard.totalSessions': 'Total sessions',\n 'dashboard.dateToday2': 'Today',\n 'dashboard.date7days': '7 days',\n 'dashboard.date30days': '30 days',\n 'dashboard.dateAlltime': 'All time',\n 'dashboard.refreshFailed': 'Refresh failed',\n 'dashboard.scanned': 'scanned {n} logs in {seconds}s',\n 'dashboard.codexCurrentOnly': 'current account only',\n 'dashboard.codexSavedOnly': 'saved account history only',\n 'dashboard.codexAllUsage': 'all saved and legacy Codex usage',\n 'dashboard.window7d': '7-day budget',\n 'dashboard.codexLabel': 'codex · all accounts',\n 'dashboard.codexLabelPrefix': 'codex',\n 'dashboard.allAgents': 'all agents',\n 'dashboard.claudeCodeLower': 'claude code',\n 'dashboard.noSnapshot': 'no rate-limit snapshot for this account yet',\n 'dashboard.useCodexThenRefresh': 'use Codex once, then Refresh data',\n 'dashboard.switchAdminThenUse': 'switch it in Admin, use Codex once, then Refresh data',\n 'dashboard.resetting': 'resetting…',\n 'dashboard.resetsAt': 'resets at {date} (in {rel})',\n 'dashboard.short': 'all',\n\n // ── cards ─────────────────────────────────────────────────────────────\n 'card.spending.title': 'spending / consumption',\n 'card.spending.claudeCost': 'claude code total',\n 'card.spending.codexTokens': 'codex tokens',\n 'card.spending.last14d': 'last 14 days',\n 'card.spending.noCursor': 'no spending data for cursor',\n 'card.spending.legendClaude': 'claude ($)',\n 'card.spending.legendCodex': 'codex (tokens)',\n 'card.activity.title': 'activity',\n 'card.activity.streakDays': '{n}-day streak',\n 'card.activity.peakSlot': 'peak slot',\n 'card.activity.todayTimeline': \"today's timeline\",\n 'card.activity.viewPattern': 'pattern',\n 'card.activity.viewToday': 'today',\n 'card.activity.dayStreak': 'day streak',\n 'card.activity.longest': 'longest',\n 'card.activity.activeDays': 'active days',\n 'card.activity.total': 'total',\n 'card.activity.peakSlotFmt': 'peak slot: {day} {hour}:00 ({mins})',\n 'card.activity.dayMon': 'Mon',\n 'card.activity.dayTue': 'Tue',\n 'card.activity.dayWed': 'Wed',\n 'card.activity.dayThu': 'Thu',\n 'card.activity.dayFri': 'Fri',\n 'card.activity.daySat': 'Sat',\n 'card.activity.daySun': 'Sun',\n 'card.activity.less': 'less',\n 'card.activity.more': 'more',\n 'card.activity.dayHeader': '{date} · {n} sessions · {hours}h active',\n 'card.activity.noToday': 'no sessions today',\n 'card.achievements.title': 'achievements',\n 'card.achievements.unlocked': '{n} / {total} unlocked',\n 'card.achievements.showLess': 'show less',\n 'card.achievements.showAll': 'show all ({n})',\n 'achv.sessions-100.title': 'Centurion', 'achv.sessions-100.desc': '100 sessions logged',\n 'achv.sessions-500.title': 'Habituated', 'achv.sessions-500.desc': '500 sessions logged',\n 'achv.sessions-1000.title': 'Veteran', 'achv.sessions-1000.desc': '1000 sessions logged',\n 'achv.streak-7.title': 'Week streak', 'achv.streak-7.desc': '7-day coding streak',\n 'achv.streak-30.title': 'Month streak', 'achv.streak-30.desc': '30-day coding streak',\n 'achv.spend-10.title': 'First $10', 'achv.spend-10.desc': 'Claude spend $10+',\n 'achv.spend-100.title': 'Big spender', 'achv.spend-100.desc': 'Claude spend $100+',\n 'achv.tokens-100m.title': 'Token rich', 'achv.tokens-100m.desc': '100M Codex tokens',\n 'achv.tokens-1b.title': 'Token tycoon', 'achv.tokens-1b.desc': '1B Codex tokens',\n 'achv.marathon-4h.title': 'Marathoner', 'achv.marathon-4h.desc': 'Single session 4h+',\n 'achv.marathon-8h.title': 'All-nighter', 'achv.marathon-8h.desc': 'Single session 8h+',\n 'achv.projects-10.title': 'Polymath', 'achv.projects-10.desc': 'Worked on 10+ projects',\n 'achv.projects-30.title': 'Jack of all', 'achv.projects-30.desc': 'Worked on 30+ projects',\n 'achv.night-owl.title': 'Night owl', 'achv.night-owl.desc': 'Past-midnight sessions 10+',\n 'achv.early-bird.title': 'Early bird', 'achv.early-bird.desc': 'Pre-7am sessions 5+',\n 'achv.multi-tool.title': 'Multi-tool', 'achv.multi-tool.desc': 'Used 3 different tools',\n 'card.burndown.title': '7-day burndown',\n 'card.burndown.header': 'usage history (7d) · {label}',\n 'card.burndown.notEnough': 'not enough data yet',\n 'card.burndown.tipUsed': 'used',\n 'card.burndown.legend5h': '5h',\n 'card.burndown.legend7d': '7day',\n 'card.leaderboard.title': 'project leaderboard',\n 'card.leaderboard.hours': 'hours',\n 'card.leaderboard.sessions': 'sessions',\n 'card.leaderboard.tools': 'tools',\n 'card.leaderboard.noProjects': 'no projects yet',\n 'card.hotspots.title': 'file hotspots',\n 'card.hotspots.sessions': 'sessions',\n 'card.toolSplit.title': 'tool split',\n 'card.toolSplit.sessions': 'sessions',\n 'card.toolSplit.totalSessions': '{n} total sessions',\n 'card.sessions.title': 'Sessions',\n 'card.sessions.search': 'search project / title…',\n 'card.sessions.tag': 'tag',\n 'card.sessions.tool': 'tool',\n 'card.sessions.project': 'project · title',\n 'card.sessions.duration': 'dur',\n 'card.sessions.started': 'started',\n 'card.sessions.status': 'status',\n 'card.sessions.tags': 'tags',\n 'card.sessions.recent': 'Recent Sessions',\n 'card.sessions.activeCount': '{active} active · {shown}/{total}',\n 'card.sessions.noMatch': 'No sessions match.',\n 'card.sessions.active': 'active',\n 'card.sessions.done': 'done',\n 'card.sessions.tagPlaceholder': 'tag…',\n\n // ── admin ─────────────────────────────────────────────────────────────\n 'admin.title': 'Vibemeter Admin',\n 'admin.subtitle': 'local Codex account switching · tokens stay on this machine',\n 'admin.activeAccount': 'Active Codex account',\n 'admin.importCurrent': 'Save current Codex login',\n 'admin.importing': 'Saving…',\n 'admin.switch': 'Switch',\n 'admin.switching': 'Switching…',\n 'admin.deleting': 'Deleting…',\n 'admin.savedAccounts': 'Saved accounts',\n 'admin.lastRefresh': 'last refresh',\n 'admin.activeAccountLabel': 'Active Codex account',\n 'admin.plan': 'plan',\n 'admin.accountId': 'account id',\n 'admin.accessToken': 'access token',\n 'admin.expires': 'expires {date}',\n 'admin.noAuth': 'No readable Codex auth found at ~/.codex/auth.json.',\n 'admin.localStore': 'Local account store',\n 'admin.localStoreHint': 'Saved snapshots live in {path}. Tokens are stored on disk for switching, but this page only renders decoded metadata.',\n 'admin.colAccount': 'account',\n 'admin.colPlan': 'plan',\n 'admin.colTokens': 'tokens',\n 'admin.colActions': 'actions',\n 'admin.noSaved': 'No saved accounts yet. Log into Codex, then save the current login here.',\n 'admin.active': 'active',\n 'admin.accessFmt': 'access {date}',\n 'admin.savedFmt': 'saved {date}',\n 'admin.importSuccess': 'Current Codex login saved to the account list.',\n 'admin.switchSuccess': 'Codex auth.json switched. Restart active Codex sessions to use the new account.',\n 'admin.deleteSuccess': 'Saved account removed.',\n\n // ── settings page ─────────────────────────────────────────────────────\n 'settings.title': 'Vibemeter Settings',\n 'settings.subtitle': 'local preferences · nothing leaves this machine',\n\n // ── settings · notify ─────────────────────────────────────────────────\n 'notify.title': 'Voice + system notifications',\n 'notify.subtitle': \"macOS only. When an agent finishes you'll hear a short \\\"{tool} {project} done\\\" and see a banner. ~/.claude/settings.json and ~/.codex/config.toml are backed up before any change.\",\n 'notify.on': 'on',\n 'notify.off': 'off',\n 'notify.claudeStop': 'Claude · Stop',\n 'notify.claudeNotification': 'Claude · Notification',\n 'notify.codexComplete': 'Codex · complete',\n 'notify.foreignNotifyWarning': \"Codex already has a different notify command. Vibemeter will not overwrite it:\",\n 'notify.foreignNotifyHint': \"Remove that line manually if you want Vibemeter to take over.\",\n 'notify.enable': 'Enable voice notifications',\n 'notify.reapply': 'Re-apply',\n 'notify.disable': 'Disable',\n 'notify.scriptMissing': 'Speaker script not found at {path}. Reinstall @hirra/vibemeter.',\n 'notify.hookStop': 'Claude Code Stop hook',\n 'notify.hookNotification': 'Claude Code Notification hook (when permission needed)',\n 'notify.hookCodex': 'Codex notify ({state})',\n 'notify.hookCodexConfigFound': 'config found',\n 'notify.hookCodexConfigMissing': 'no ~/.codex/config.toml',\n 'notify.foreignWarn': 'foreign notify exists',\n 'notify.foreignBody': 'Codex already has a different notify command. Vibemeter will **not** overwrite it:',\n 'notify.foreignRemove': 'Remove that line manually if you want Vibemeter to take over.',\n 'notify.installing': 'Enabling…',\n 'notify.removing': 'Removing…',\n 'notify.skipNoCodex': 'Codex config not found — skipped',\n 'notify.skipForeign': 'Codex notify already set to a different command, left alone: {cmd}',\n 'notify.updated': 'Updated. {notes}',\n 'notify.enabledMsg': 'Voice notifications enabled.',\n 'notify.disabledMsg': 'Voice notifications removed.',\n\n // ── settings · alerts ─────────────────────────────────────────────────\n 'alerts.title': 'Quota push alerts',\n 'alerts.subtitle': 'threshold trips, daily summary, 5h/weekly reset reminders — push to WeChat Work bot or any webhook. Webhook is written to {path} only — {warn}',\n 'alerts.subtitleWarn': 'never reaches GitHub',\n 'alerts.sectionChannels': 'Push channels',\n 'alerts.sectionRules': 'Trigger rules',\n 'alerts.addChannelWxwork': '+ WeChat Work',\n 'alerts.addChannelGeneric': '+ Generic webhook',\n 'alerts.addRuleThreshold': '+ Threshold',\n 'alerts.addRuleDaily': '+ Daily',\n 'alerts.addRuleReset': '+ Reset reminder',\n 'alerts.noChannels': 'No channels yet.',\n 'alerts.noRules': 'No rules yet.',\n 'alerts.testBtn': 'Test',\n 'alerts.testing': 'Testing…',\n 'alerts.sendNow': 'Send summary now',\n 'alerts.sending': 'Sending…',\n 'alerts.delete': 'Delete',\n 'alerts.webhookLabel': 'Webhook URL',\n 'alerts.webhookHintWxwork': '— qyapi.weixin.qq.com/cgi-bin/webhook/send?key=…',\n 'alerts.webhookPlaceholderEdit': 'Paste the full webhook URL',\n 'alerts.webhookPlaceholderClick': 'Click to edit',\n 'alerts.webhookStored': 'Stored — editing replaces; leave alone to keep current.',\n 'alerts.editFirst': 'Save first before testing',\n 'alerts.saveBtn': 'Save',\n 'alerts.saving': 'Saving…',\n 'alerts.runAllNow': 'Evaluate all rules now',\n 'alerts.evaluating': 'Evaluating…',\n 'alerts.savedHint': 'Saved. Webhook is only written to ~/.vibemeter/alerts.json — never uploaded to GitHub.',\n 'alerts.testSuccess': 'Test message delivered ({channel}, {attempts} attempt(s))',\n 'alerts.summarySuccess': \"Today's summary delivered ({channel}, {attempts} attempt(s))\",\n 'alerts.pushFail': 'Push failed: {message}',\n 'alerts.evalDone': 'Done — {fired} push(es) fired, {evaluated} enabled rule(s)',\n 'alerts.kindThreshold': 'threshold',\n 'alerts.kindDaily': 'daily',\n 'alerts.kindReset': 'reset',\n 'alerts.enabled': 'enabled',\n 'alerts.metric': 'Metric',\n 'alerts.below': 'Below',\n 'alerts.pushTime': 'Push at',\n 'alerts.pushTimeHint': '(local time, at most one per day)',\n 'alerts.window': 'Window',\n 'alerts.minutesBefore': 'Lead',\n 'alerts.minutesBeforeHint': 'min (60=1h, 1440=1d)',\n 'alerts.remainingAbove': 'Remaining ≥',\n 'alerts.remainingAboveHint': '% (only nudges while quota is worth saving)',\n 'alerts.pushTo': 'Push to',\n 'alerts.addChannelFirst': 'Add a channel first',\n 'alerts.pushLocale': 'Push language',\n 'alerts.pushLocaleHint': 'Language used in pushed messages (independent of UI language)',\n 'alerts.pushLocaleZh': '中文',\n 'alerts.pushLocaleEn': 'English',\n 'alerts.defaultLabelWxwork': 'WeChat Work bot',\n 'alerts.defaultLabelGeneric': 'Generic webhook',\n 'alerts.testMsgWord': 'Test message',\n 'alerts.summaryWord': \"Today's summary\",\n 'alerts.pushFailUnknown': 'unknown error',\n 'alerts.metric.claude_5h_remaining_pct': 'Claude Code · 5h remaining',\n 'alerts.metric.claude_weekly_remaining_pct': 'Claude Code · weekly remaining',\n 'alerts.metric.codex_5h_remaining_pct': 'Codex · 5h remaining',\n 'alerts.metric.codex_weekly_remaining_pct': 'Codex · weekly remaining',\n 'alerts.resetMetric.claude_5h': 'Claude Code · 5h window',\n 'alerts.resetMetric.claude_weekly': 'Claude Code · weekly window',\n 'alerts.resetMetric.codex_5h': 'Codex · 5h window',\n 'alerts.resetMetric.codex_weekly': 'Codex · weekly window',\n\n // ── settings · donate ─────────────────────────────────────────────────\n 'donate.title': 'Tip the author',\n 'donate.subtitle': 'Vibemeter is fully local and free. If it saves you time, a small tip keeps the project alive — no obligation.',\n 'donate.alipay': 'Alipay',\n 'donate.zoomClose': 'Close',\n 'donate.aside': 'Scan and pay any amount. Drop a note about the dashboard view you use the most — that drives the next iteration.',\n 'donate.missing': 'QR image not bundled yet — drop it at {path}.',\n\n // ── float widget ──────────────────────────────────────────────────────\n 'float.unknownReset': '未知重置时间',\n 'float.snapshotExpired': '快照已过期',\n 'float.noSnapshot': '尚无快照',\n 'float.justNow': '刚刚',\n 'float.minAgo': '{n} 分钟前',\n 'float.hourAgo': '{n} 小时前',\n 'float.currentQuota': '当前额度',\n 'float.noQuota': '无额度',\n 'float.fiveHRemain': '5h 剩余',\n 'float.usedPct': '已用 {pct}',\n 'float.usedNo': '尚无数据',\n 'float.refresh': '刷新',\n 'float.refreshing': '刷新中',\n 'float.statToday': '今日',\n 'float.statTotal': '累计',\n 'float.statWeekly': '本周',\n 'float.noToday': '今天还没有会话',\n 'float.latest': '最近',\n};\n\nconst MESSAGES: Record<Locale, Messages> = { zh, en };\n\nexport function messagesFor(locale: Locale): Messages {\n return MESSAGES[locale] ?? MESSAGES.zh;\n}\n","// Pure translator. Lookup by key + optional {name} interpolation. Works in\n// both server and client contexts — caller supplies the locale explicitly so\n// we don't need to thread next/headers into the hot path.\n\nimport { messagesFor } from './messages';\nimport type { Locale } from './types';\n\nexport type { Locale } from './types';\nexport { DEFAULT_LOCALE, LOCALE_COOKIE, LOCALES, isLocale, normalizeLocale } from './types';\nexport { messagesFor } from './messages';\n\nexport function t(locale: Locale, key: string, vars?: Record<string, string | number>): string {\n const msgs = messagesFor(locale);\n const tpl = msgs[key] ?? key;\n if (!vars) return tpl;\n return tpl.replace(/\\{(\\w+)\\}/g, (_, name) => {\n const v = vars[name];\n return v == null ? `{${name}}` : String(v);\n });\n}\n"],"names":["LOCALES","DEFAULT_LOCALE","LOCALE_COOKIE","isLocale","v","normalizeLocale"],"mappings":"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"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["chunks/_0-m5tdn._.js",32714,e=>{"use strict";async function t(){}e.s(["register",0,t])},85841,(e,t,n)=>{self._ENTRIES||={};let s=Promise.resolve().then(()=>e.i(32714));s.catch(()=>{}),self._ENTRIES.middleware_instrumentation=new Proxy(s,{get(e,t){if("then"===t)return(t,n)=>e.then(t,n);let n=(...n)=>e.then(e=>(0,e[t])(...n));return n.then=(n,s)=>e.then(e=>e[t]).then(n,s),n}})}]);
|
|
2
|
+
|
|
3
|
+
//# sourceMappingURL=_0-m5tdn._.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["turbopack:///[project]/src/instrumentation.ts","turbopack:///[project]/node_modules/next/src/build/templates/edge-wrapper.js"],"sourcesContent":["// Next.js instrumentation hook — runs once per server boot. Used to start\n// the alerts ticker so quota thresholds and daily summaries fire even when\n// no one is hitting the dashboard.\n\nexport async function register() {\n // Edge runtime has no setInterval/long-lived state; only boot the ticker\n // on the Node.js server runtime where the dashboard actually runs.\n if (process.env.NEXT_RUNTIME !== 'nodejs') return;\n const { startAlertsTicker } = await import('@/lib/alerts/ticker');\n startAlertsTicker();\n}\n","// The wrapped module could be an async module, we handle that with the proxy\n// here. The comma expression makes sure we don't call the function with the\n// module as the \"this\" arg.\n// Turn exports into functions that are also a thenable. This way you can await the whole object\n// or exports (e.g. for Components) or call them directly as though they are async functions\n// (e.g. edge functions/middleware, this is what the Edge Runtime does).\n// Catch promise to prevent UnhandledPromiseRejectionWarning, this will be propagated through\n// the awaited export(s) anyway.\nself._ENTRIES ||= {}\nconst modProm = import('MODULE')\nmodProm.catch(() => {})\nself._ENTRIES['VAR_ENTRY_NAME'] = new Proxy(modProm, {\n get(innerModProm, name) {\n if (name === 'then') {\n return (res, rej) => innerModProm.then(res, rej)\n }\n let result = (...args) =>\n innerModProm.then((mod) => (0, mod[name])(...args))\n result.then = (res, rej) =>\n innerModProm.then((mod) => mod[name]).then(res, rej)\n return result\n },\n})\n"],"names":["register","startAlertsTicker","self","_ENTRIES","modProm","catch","Proxy","get","innerModProm","name","res","rej","then","result","args","mod"],"mappings":"sGAIO,eAAeA,IAMtB,wCCFAE,KAAKC,QAAQ,GAAK,CAAC,EACnB,IAAMC,EAAU,MAAM,CAAC,CAAA,OAAA,GAAA,IAAA,CAAA,IAAA,EAAA,CAAA,CAAA,QACvBA,EAAQC,KAAK,CAAC,KAAO,GACrBH,KAAKC,QAAQ,CAAC,iBAAiB,GAAG,IAAIG,EAAAA,CAAMF,IAAAA,GAAS,GAAA,EAAA,CACnDG,IAAIC,CAAY,CAAEC,CAAI,EACpB,GAAa,QAAQ,CAAjBA,EACF,MAAO,CAACC,EAAKC,IAAQH,EAAaI,IAAI,CAACF,EAAKC,GAE9C,IAAIE,EAAS,CAAC,GAAGC,IACfN,EAAaI,IAAI,CAAC,AAACG,GAAS,CAAA,EAAGA,CAAG,CAACN,EAAAA,AAAI,KAAMK,IAG/C,OAFAD,EAAOD,IAAI,CAAG,CAACF,EAAKC,IAClBH,EAAaI,IAAI,CAAC,AAACG,GAAQA,CAAG,CAACN,EAAK,EAAEG,IAAI,CAACF,EAAKC,GAC3CE,CACT,CACF","ignoreList":[1]}
|