comfyui-frontend-package 1.38.5__py3-none-any.whl → 1.38.6__py3-none-any.whl
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.
- comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js → AboutPanel-lkjGFasi.js} +2 -2
- comfyui_frontend_package/static/assets/{AboutPanel-CHse5rOA.js.map → AboutPanel-lkjGFasi.js.map} +1 -1
- comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js → AudioPreviewPlayer-BxCSKPl9.js} +2 -2
- comfyui_frontend_package/static/assets/{AudioPreviewPlayer-CAa8V66L.js.map → AudioPreviewPlayer-BxCSKPl9.js.map} +1 -1
- comfyui_frontend_package/static/assets/AudioPreviewPlayer-CkxKvcVf.js +1 -0
- comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js → BaseViewTemplate-CjODF2hh.js} +2 -2
- comfyui_frontend_package/static/assets/{BaseViewTemplate-DA6zfigT.js.map → BaseViewTemplate-CjODF2hh.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js → CloudAuthTimeoutView-D-QkjPNh.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudAuthTimeoutView-9OPBS1hE.js.map → CloudAuthTimeoutView-D-QkjPNh.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js → CloudBadge-B4nmLus2.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudBadge-BnLiAHDN.js.map → CloudBadge-B4nmLus2.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js → CloudForgotPasswordView-DOEV9hGr.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudForgotPasswordView-BqDR_C7K.js.map → CloudForgotPasswordView-DOEV9hGr.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js → CloudLayoutView-ShKH6rRV.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudLayoutView-vTrrVUOY.js.map → CloudLayoutView-ShKH6rRV.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js → CloudLoginView-C3Te42U9.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudLoginView-T17euJly.js.map → CloudLoginView-C3Te42U9.js.map} +1 -1
- comfyui_frontend_package/static/assets/CloudRunButtonWrapper-Cub7EB34.js +3 -0
- comfyui_frontend_package/static/assets/{CloudRunButtonWrapper-hQc4BNkX.js.map → CloudRunButtonWrapper-Cub7EB34.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js → CloudSignupView-X2oiL3ZR.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSignupView-vEDby5k3.js.map → CloudSignupView-X2oiL3ZR.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js → CloudSubscriptionRedirectView-UjNv8emo.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSubscriptionRedirectView-DPyO745g.js.map → CloudSubscriptionRedirectView-UjNv8emo.js.map} +1 -1
- comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js → CloudSurveyView-IaiucCTP.js} +2 -2
- comfyui_frontend_package/static/assets/{CloudSurveyView-CBtTd9Ru.js.map → CloudSurveyView-IaiucCTP.js.map} +1 -1
- comfyui_frontend_package/static/assets/ComfyQueueButton-BppnHbrl.js +1 -0
- comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js → ComfyQueueButton-HjSIKZKO.js} +2 -2
- comfyui_frontend_package/static/assets/{ComfyQueueButton-MZrp7wYJ.js.map → ComfyQueueButton-HjSIKZKO.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js → ExtensionPanel-Bzb9QtKj.js} +2 -2
- comfyui_frontend_package/static/assets/{ExtensionPanel-CrWVGUtg.js.map → ExtensionPanel-Bzb9QtKj.js.map} +1 -1
- comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js → GlobalToast-BSCvu6Hw.js} +2 -2
- comfyui_frontend_package/static/assets/{GlobalToast-BiCmpIvO.js.map → GlobalToast-BSCvu6Hw.js.map} +1 -1
- comfyui_frontend_package/static/assets/{GraphView-BCkpNGgz.js → GraphView-gYVCtm1V.js} +5 -5
- comfyui_frontend_package/static/assets/GraphView-gYVCtm1V.js.map +1 -0
- comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js → KeybindingPanel-DF-bG4iO.js} +2 -2
- comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js.map → KeybindingPanel-DF-bG4iO.js.map} +1 -1
- comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js → LegacyCreditsPanel-D-CboO8k.js} +2 -2
- comfyui_frontend_package/static/assets/{LegacyCreditsPanel-6vR8koQy.js.map → LegacyCreditsPanel-D-CboO8k.js.map} +1 -1
- comfyui_frontend_package/static/assets/Load3D-c9UwgGoI.js +1 -0
- comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js → Load3D-vYr8M3jJ.js} +2 -2
- comfyui_frontend_package/static/assets/{Load3D-ei1BUF9O.js.map → Load3D-vYr8M3jJ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js → PanelTemplate-BJda9e5J.js} +2 -2
- comfyui_frontend_package/static/assets/{PanelTemplate-BjN5XNg2.js.map → PanelTemplate-BJda9e5J.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js → ServerConfigPanel-VsC6xlZJ.js} +2 -2
- comfyui_frontend_package/static/assets/{ServerConfigPanel-CxovH9Qk.js.map → ServerConfigPanel-VsC6xlZJ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js → SubscribeButton-DZBycfCA.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscribeButton-CTOQRkfg.js.map → SubscribeButton-DZBycfCA.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js → SubscribeToRun-4YolxBOL.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscribeToRun-DnXzV8y0.js.map → SubscribeToRun-4YolxBOL.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js → SubscriptionPanel-B6txX4Vm.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscriptionPanel-D9uv7z8f.js.map → SubscriptionPanel-B6txX4Vm.js.map} +1 -1
- comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js → SubscriptionRequiredDialogContent-COEF2VQ_.js} +2 -2
- comfyui_frontend_package/static/assets/{SubscriptionRequiredDialogContent--VmT16oc.js.map → SubscriptionRequiredDialogContent-COEF2VQ_.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js → UserCheckView-x-fkcYzc.js} +2 -2
- comfyui_frontend_package/static/assets/{UserCheckView-spD3LyMu.js.map → UserCheckView-x-fkcYzc.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js → UserPanel-7N9QknQj.js} +2 -2
- comfyui_frontend_package/static/assets/{UserPanel-Su6NtJ5q.js.map → UserPanel-7N9QknQj.js.map} +1 -1
- comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js → UserSelectView-BYjOkfSa.js} +2 -2
- comfyui_frontend_package/static/assets/{UserSelectView-C5LBOPcv.js.map → UserSelectView-BYjOkfSa.js.map} +1 -1
- comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js → ValueControlPopover-BPAa35QG.js} +2 -2
- comfyui_frontend_package/static/assets/{ValueControlPopover-BdlDzT8l.js.map → ValueControlPopover-BPAa35QG.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js → WidgetAudioUI-Dw-r3Ews.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetAudioUI-BDZxDx_r.js.map → WidgetAudioUI-Dw-r3Ews.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js → WidgetImageCrop-kERy9g5I.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetImageCrop-CYRW7t2Q.js.map → WidgetImageCrop-kERy9g5I.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetInputNumber-BOKO36G3.js → WidgetInputNumber-BaClCNAC.js} +1 -1
- comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js +3 -0
- comfyui_frontend_package/static/assets/WidgetInputNumber-DU_D0Fzy.js.map +1 -0
- comfyui_frontend_package/static/assets/{WidgetLegacy-Bslv9wZZ.js → WidgetLegacy-B4nipUM9.js} +1 -1
- comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js → WidgetRecordAudio-Nk8dH238.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetRecordAudio-Bzy8PIzN.js.map → WidgetRecordAudio-Nk8dH238.js.map} +1 -1
- comfyui_frontend_package/static/assets/WidgetSelect-DzZPpO_-.js +1 -0
- comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js → WidgetSelect-nSQrk_hd.js} +2 -2
- comfyui_frontend_package/static/assets/{WidgetSelect-zgrFVzHH.js.map → WidgetSelect-nSQrk_hd.js.map} +1 -1
- comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js → WidgetWithControl-Da6zUB5e.js} +3 -3
- comfyui_frontend_package/static/assets/{WidgetWithControl-Dh2FWOiA.js.map → WidgetWithControl-Da6zUB5e.js.map} +1 -1
- comfyui_frontend_package/static/assets/{api-CUAc7rDA.js → api-Dwq2LQIW.js} +4 -4
- comfyui_frontend_package/static/assets/api-Dwq2LQIW.js.map +1 -0
- comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js → audioService-DvVaKhuU.js} +2 -2
- comfyui_frontend_package/static/assets/{audioService-DvndbCi2.js.map → audioService-DvVaKhuU.js.map} +1 -1
- comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js → audioUtils-DD4rUYVZ.js} +2 -2
- comfyui_frontend_package/static/assets/{audioUtils-DpjpcKbH.js.map → audioUtils-DD4rUYVZ.js.map} +1 -1
- comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js → auth-B9axG-yZ.js} +2 -2
- comfyui_frontend_package/static/assets/{auth-B8ZZ0KKQ.js.map → auth-B9axG-yZ.js.map} +1 -1
- comfyui_frontend_package/static/assets/auth-D74DTev8.js +1 -0
- comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js → cloudBadges-D5mGJbRy.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudBadges-C1a7fBky.js.map → cloudBadges-D5mGJbRy.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js → cloudFeedbackTopbarButton-RZUssOmb.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudFeedbackTopbarButton-DR0T8sWG.js.map → cloudFeedbackTopbarButton-RZUssOmb.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js → cloudRemoteConfig-F9J0iGyF.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudRemoteConfig-DhMjC5TB.js.map → cloudRemoteConfig-F9J0iGyF.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js → cloudSessionCookie-MEORlqtg.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudSessionCookie-Duxk6ux1.js.map → cloudSessionCookie-MEORlqtg.js.map} +1 -1
- comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js → cloudSubscription-CuWNXKVx.js} +2 -2
- comfyui_frontend_package/static/assets/{cloudSubscription-B8l6B9Nx.js.map → cloudSubscription-CuWNXKVx.js.map} +1 -1
- comfyui_frontend_package/static/assets/{core-DBfeqMDR.js → core-IYu8XAIx.js} +4 -4
- comfyui_frontend_package/static/assets/{core-DBfeqMDR.js.map → core-IYu8XAIx.js.map} +1 -1
- comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js → dialogService-YG0RH337.js} +9 -9
- comfyui_frontend_package/static/assets/{dialogService-BZ1FmjZL.js.map → dialogService-YG0RH337.js.map} +1 -1
- comfyui_frontend_package/static/assets/firebaseAuthStore-DnNaPbuZ.js +1 -0
- comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js → graphHasMissingNodes-BhD1N6zI.js} +2 -2
- comfyui_frontend_package/static/assets/{graphHasMissingNodes-C79Wi51S.js.map → graphHasMissingNodes-BhD1N6zI.js.map} +1 -1
- comfyui_frontend_package/static/assets/{index-CGxJFSof.js → index-BMy3twho.js} +3 -3
- comfyui_frontend_package/static/assets/{index-CGxJFSof.js.map → index-BMy3twho.js.map} +1 -1
- comfyui_frontend_package/static/assets/{index-DNpOhRra.css → index-KMO9qFHH.css} +1 -1
- comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js → keybindingService-CBLPjYHI.js} +2 -2
- comfyui_frontend_package/static/assets/{keybindingService-B88NjeAU.js.map → keybindingService-CBLPjYHI.js.map} +1 -1
- comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js → releaseStore-DDOxzkVb.js} +2 -2
- comfyui_frontend_package/static/assets/{releaseStore-x0vHjxrw.js.map → releaseStore-DDOxzkVb.js.map} +1 -1
- comfyui_frontend_package/static/assets/releaseStore-iVkqunL8.js +1 -0
- comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js → subscriptionCheckoutUtil-DswSOreM.js} +2 -2
- comfyui_frontend_package/static/assets/{subscriptionCheckoutUtil-B_OvUP2T.js.map → subscriptionCheckoutUtil-DswSOreM.js.map} +1 -1
- comfyui_frontend_package/static/assets/{useCurrentUser-BJcn2Vgo.js → useCurrentUser-NdaCJzIK.js} +1 -1
- comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js → useErrorHandling-Cfa5N_7c.js} +2 -2
- comfyui_frontend_package/static/assets/{useErrorHandling-CI8_F4yx.js.map → useErrorHandling-Cfa5N_7c.js.map} +1 -1
- comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js → useSubscriptionDialog-792qfEJ2.js} +3 -3
- comfyui_frontend_package/static/assets/{useSubscriptionDialog-Chxkdny5.js.map → useSubscriptionDialog-792qfEJ2.js.map} +1 -1
- comfyui_frontend_package/static/assets/useSubscriptionDialog-B-eGeK3j.js +1 -0
- comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js → userStore-BAS9m9W6.js} +2 -2
- comfyui_frontend_package/static/assets/{userStore-BkgQPjq6.js.map → userStore-BAS9m9W6.js.map} +1 -1
- comfyui_frontend_package/static/assets/vendor-three-BFcUNSs9.js.map +1 -1
- comfyui_frontend_package/static/index.html +1 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/METADATA +1 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/RECORD +126 -126
- comfyui_frontend_package/static/assets/AudioPreviewPlayer-BoEdyGI_.js +0 -1
- comfyui_frontend_package/static/assets/CloudRunButtonWrapper-hQc4BNkX.js +0 -3
- comfyui_frontend_package/static/assets/ComfyQueueButton-BbQnRThI.js +0 -1
- comfyui_frontend_package/static/assets/GraphView-BCkpNGgz.js.map +0 -1
- comfyui_frontend_package/static/assets/Load3D-DHBmC_AU.js +0 -1
- comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js +0 -3
- comfyui_frontend_package/static/assets/WidgetInputNumber-DGKypM5j.js.map +0 -1
- comfyui_frontend_package/static/assets/WidgetSelect-DsJGH12l.js +0 -1
- comfyui_frontend_package/static/assets/api-CUAc7rDA.js.map +0 -1
- comfyui_frontend_package/static/assets/auth-D3RiiqZ8.js +0 -1
- comfyui_frontend_package/static/assets/firebaseAuthStore-CZgxeMyf.js +0 -1
- comfyui_frontend_package/static/assets/releaseStore-CubqSv5t.js +0 -1
- comfyui_frontend_package/static/assets/useSubscriptionDialog-BzMzio2H.js +0 -1
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/WHEEL +0 -0
- {comfyui_frontend_package-1.38.5.dist-info → comfyui_frontend_package-1.38.6.dist-info}/top_level.txt +0 -0
comfyui_frontend_package/static/assets/{KeybindingPanel-CAXL5TlV.js → KeybindingPanel-DF-bG4iO.js}
RENAMED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var L=Object.defineProperty;var s=(K,y)=>L(K,"name",{value:y,configurable:!0});import{L as C,N as J,Q,_t as R,c as E,it as X,mt as Y,ut as G}from"./vendor-primevue-DcMRXJN3.js";import{$o as c,Ba as r,Do as l,Ha as U,Jo as t,Ka as $,Ma as W,Oo as Z,Pa as A,To as P,Ua as k,Va as w,Xo as ee,Ya as z,do as u,go as ie,po as ae,qa as n,za as S,zo as b}from"./vendor-other-DlQF6V2E.js";import{n as le}from"./vendor-vue-D9IUuwPJ.js";import"./api-
|
|
1
|
+
var L=Object.defineProperty;var s=(K,y)=>L(K,"name",{value:y,configurable:!0});import{L as C,N as J,Q,_t as R,c as E,it as X,mt as Y,ut as G}from"./vendor-primevue-DcMRXJN3.js";import{$o as c,Ba as r,Do as l,Ha as U,Jo as t,Ka as $,Ma as W,Oo as Z,Pa as A,To as P,Ua as k,Va as w,Xo as ee,Ya as z,do as u,go as ie,po as ae,qa as n,za as S,zo as b}from"./vendor-other-DlQF6V2E.js";import{n as le}from"./vendor-vue-D9IUuwPJ.js";import"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-Cfa5N_7c.js";import{t as g}from"./Button-Do2I1OAA.js";import{t as ne,x as te}from"./PanelTemplate-BJda9e5J.js";import{Ar as se,Or as oe,ir as de,jr as re,kr as ue}from"./dialogService-YG0RH337.js";import{t as ye}from"./_plugin-vue_export-helper-PHE0iSCb.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BAS9m9W6.js";import{t as pe}from"./keybindingService-CBLPjYHI.js";var be={key:0,class:"px-2"},ce=z({__name:"KeyComboDisplay",props:{keyCombo:{},isModified:{type:Boolean,default:!1}},setup(K){const y=S(()=>K.keyCombo.getKeySequences());return(o,m)=>(u(),k("span",null,[(u(!0),k(A,null,ae(y.value,(h,_)=>(u(),k(A,{key:_},[n(t(E),{severity:o.isModified?"info":"secondary"},{default:l(()=>[$(c(h),1)]),_:2},1032,["severity"]),_<y.value.length-1?(u(),k("span",be,"+")):U("",!0)],64))),128))]))}}),me=ce,ve={class:"actions invisible flex flex-row"},fe=["title"],ge={key:1},ke={class:"overflow-hidden text-ellipsis"},Ke=z({__name:"KeybindingPanel",setup(K){const y=b({global:{value:"",matchMode:R.CONTAINS}}),o=re(),m=pe(),h=oe(),{t:_}=le(),D=S(()=>Object.values(h.commands).map(e=>({id:e.id,label:_(`commands.${te(e.id)}.label`,e.label??""),keybinding:o.getKeybindingByCommandId(e.id),source:e.source}))),B=b(null),v=b(!1),d=b(null),p=b(null),M=b(null),f=S(()=>!p.value||p.value.keybinding?.combo?.equals(d.value)||!d.value?null:o.getKeybinding(d.value));function N(e){p.value=e,d.value=e.keybinding?e.keybinding.combo:null,v.value=!0}s(N,"editKeybinding"),P(()=>{v.value&&setTimeout(()=>{M.value?.$el?.focus()},300)});async function T(e){e.keybinding&&(o.unsetKeybinding(e.keybinding),await m.persistUserKeybindings())}s(T,"removeKeybinding");async function O(e){if(!e.shiftKey&&!e.altKey&&!e.ctrlKey&&!e.metaKey)switch(e.key){case"Escape":I();return;case"Enter":await V();return}d.value=ue.fromEvent(e)}s(O,"captureKeybinding");function I(){v.value=!1,p.value=null,d.value=null}s(I,"cancelEdit");async function V(){p.value&&d.value&&o.updateKeybindingOnCommand(new se({commandId:p.value.id,combo:d.value}))&&await m.persistUserKeybindings(),I()}s(V,"saveKeybinding");async function q(e){o.resetKeybindingForCommand(e.id)?await m.persistUserKeybindings():console.warn(`No changes made when resetting keybinding for command: ${e.id}`)}s(q,"resetKeybinding");const F=Y();async function j(){o.resetAllKeybindings(),await m.persistUserKeybindings(),F.add({severity:"info",summary:"Info",detail:"All keybindings reset",life:3e3})}return s(j,"resetAllKeybindings"),(e,a)=>{const H=ie("tooltip");return u(),w(ne,{value:"Keybinding",class:"keybinding-panel"},{header:l(()=>[n(de,{modelValue:y.value.global.value,"onUpdate:modelValue":a[0]||(a[0]=i=>y.value.global.value=i),placeholder:e.$t("g.searchKeybindings")+"..."},null,8,["modelValue","placeholder"])]),default:l(()=>[n(t(J),{selection:B.value,"onUpdate:selection":a[1]||(a[1]=i=>B.value=i),value:D.value,"global-filter-fields":["id","label"],filters:y.value,"selection-mode":"single","striped-rows":"",pt:{header:"px-0"},onRowDblclick:a[2]||(a[2]=i=>N(i.data))},{default:l(()=>[n(t(C),{field:"actions",header:""},{body:l(i=>[r("div",ve,[n(g,{variant:"textonly",size:"icon","aria-label":e.$t("g.edit"),onClick:s(x=>N(i.data),"onClick")},{default:l(()=>a[4]||(a[4]=[r("i",{class:"pi pi-pencil"},null,-1)])),_:2},1032,["aria-label","onClick"]),n(g,{variant:"textonly",size:"icon","aria-label":e.$t("g.reset"),disabled:!t(o).isCommandKeybindingModified(i.data.id),onClick:s(x=>q(i.data),"onClick")},{default:l(()=>a[5]||(a[5]=[r("i",{class:"pi pi-replay"},null,-1)])),_:2},1032,["aria-label","disabled","onClick"]),n(g,{variant:"textonly",size:"icon","aria-label":e.$t("g.delete"),disabled:!i.data.keybinding,onClick:s(x=>T(i.data),"onClick")},{default:l(()=>a[6]||(a[6]=[r("i",{class:"pi pi-trash"},null,-1)])),_:2},1032,["aria-label","disabled","onClick"])])]),_:1}),n(t(C),{field:"id",header:e.$t("g.command"),sortable:"",class:"max-w-64 2xl:max-w-full"},{body:l(i=>[r("div",{class:"truncate",title:i.data.id},c(i.data.label),9,fe)]),_:1},8,["header"]),n(t(C),{field:"keybinding",header:e.$t("g.keybinding")},{body:l(i=>[i.data.keybinding?(u(),w(me,{key:0,"key-combo":i.data.keybinding.combo,"is-modified":t(o).isCommandKeybindingModified(i.data.id)},null,8,["key-combo","is-modified"])):(u(),k("span",ge,"-"))]),_:1},8,["header"]),n(t(C),{field:"source",header:e.$t("g.source")},{body:l(i=>[r("span",ke,c(i.data.source||"-"),1)]),_:1},8,["header"])]),_:1},8,["selection","value","filters"]),n(t(Q),{visible:v.value,"onUpdate:visible":a[3]||(a[3]=i=>v.value=i),class:"min-w-96",modal:"",header:p.value?.label,onHide:I},{footer:l(()=>[n(g,{variant:f.value?"destructive":"primary",autofocus:"",onClick:V},{default:l(()=>[r("i",{class:ee(f.value?"pi pi-pencil":"pi pi-check")},null,2),$(" "+c(f.value?e.$t("g.overwrite"):e.$t("g.save")),1)]),_:1},8,["variant"])]),default:l(()=>[r("div",null,[n(t(X),{ref_key:"keybindingInput",ref:M,class:"mb-2 text-center","model-value":d.value?.toString()??"",placeholder:e.$t("g.pressKeysForNewBinding"),autocomplete:"off",fluid:"",onKeydown:W(O,["stop","prevent"])},null,8,["model-value","placeholder"]),f.value?(u(),w(t(G),{key:0,severity:"warn"},{default:l(()=>[$(c(e.$t("g.keybindingAlreadyExists"))+" ",1),n(t(E),{severity:"secondary",value:f.value.commandId},null,8,["value"])]),_:1})):U("",!0)])]),_:1},8,["visible","header"]),Z((u(),w(g,{class:"mt-4 w-full",variant:"destructive-textonly",onClick:j},{default:l(()=>[a[7]||(a[7]=r("i",{class:"pi pi-replay"},null,-1)),$(" "+c(e.$t("g.resetAll")),1)]),_:1})),[[H,e.$t("g.resetAllKeybindingsTooltip")]])]),_:1})}}}),Oe=ye(Ke,[["__scopeId","data-v-06049058"]]);export{Oe as default};
|
|
2
2
|
|
|
3
|
-
//# sourceMappingURL=KeybindingPanel-
|
|
3
|
+
//# sourceMappingURL=KeybindingPanel-DF-bG4iO.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeybindingPanel-CAXL5TlV.js","names":[],"sources":["../../src/components/dialog/content/setting/keybinding/KeyComboDisplay.vue","../../src/components/dialog/content/setting/keybinding/KeyComboDisplay.vue","../../src/components/dialog/content/setting/KeybindingPanel.vue","../../src/components/dialog/content/setting/KeybindingPanel.vue"],"sourcesContent":["<template>\n <span>\n <template v-for=\"(sequence, index) in keySequences\" :key=\"index\">\n <Tag :severity=\"isModified ? 'info' : 'secondary'\">\n {{ sequence }}\n </Tag>\n <span v-if=\"index < keySequences.length - 1\" class=\"px-2\">+</span>\n </template>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\n\nimport type { KeyComboImpl } from '@/stores/keybindingStore'\n\nconst { keyCombo, isModified = false } = defineProps<{\n keyCombo: KeyComboImpl\n isModified?: boolean\n}>()\n\nconst keySequences = computed(() => keyCombo.getKeySequences())\n</script>\n","<template>\n <span>\n <template v-for=\"(sequence, index) in keySequences\" :key=\"index\">\n <Tag :severity=\"isModified ? 'info' : 'secondary'\">\n {{ sequence }}\n </Tag>\n <span v-if=\"index < keySequences.length - 1\" class=\"px-2\">+</span>\n </template>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\n\nimport type { KeyComboImpl } from '@/stores/keybindingStore'\n\nconst { keyCombo, isModified = false } = defineProps<{\n keyCombo: KeyComboImpl\n isModified?: boolean\n}>()\n\nconst keySequences = computed(() => keyCombo.getKeySequences())\n</script>\n","<template>\n <PanelTemplate value=\"Keybinding\" class=\"keybinding-panel\">\n <template #header>\n <SearchBox\n v-model=\"filters['global'].value\"\n :placeholder=\"$t('g.searchKeybindings') + '...'\"\n />\n </template>\n\n <DataTable\n v-model:selection=\"selectedCommandData\"\n :value=\"commandsData\"\n :global-filter-fields=\"['id', 'label']\"\n :filters=\"filters\"\n selection-mode=\"single\"\n striped-rows\n :pt=\"{\n header: 'px-0'\n }\"\n @row-dblclick=\"editKeybinding($event.data)\"\n >\n <Column field=\"actions\" header=\"\">\n <template #body=\"slotProps\">\n <div class=\"actions invisible flex flex-row\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.edit')\"\n @click=\"editKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-pencil\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.reset')\"\n :disabled=\"\n !keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n @click=\"resetKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-replay\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.delete')\"\n :disabled=\"!slotProps.data.keybinding\"\n @click=\"removeKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-trash\" />\n </Button>\n </div>\n </template>\n </Column>\n <Column\n field=\"id\"\n :header=\"$t('g.command')\"\n sortable\n class=\"max-w-64 2xl:max-w-full\"\n >\n <template #body=\"slotProps\">\n <div class=\"truncate\" :title=\"slotProps.data.id\">\n {{ slotProps.data.label }}\n </div>\n </template>\n </Column>\n <Column field=\"keybinding\" :header=\"$t('g.keybinding')\">\n <template #body=\"slotProps\">\n <KeyComboDisplay\n v-if=\"slotProps.data.keybinding\"\n :key-combo=\"slotProps.data.keybinding.combo\"\n :is-modified=\"\n keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n />\n <span v-else>-</span>\n </template>\n </Column>\n <Column field=\"source\" :header=\"$t('g.source')\">\n <template #body=\"slotProps\">\n <span class=\"overflow-hidden text-ellipsis\">{{\n slotProps.data.source || '-'\n }}</span>\n </template>\n </Column>\n </DataTable>\n\n <Dialog\n v-model:visible=\"editDialogVisible\"\n class=\"min-w-96\"\n modal\n :header=\"currentEditingCommand?.label\"\n @hide=\"cancelEdit\"\n >\n <div>\n <InputText\n ref=\"keybindingInput\"\n class=\"mb-2 text-center\"\n :model-value=\"newBindingKeyCombo?.toString() ?? ''\"\n :placeholder=\"$t('g.pressKeysForNewBinding')\"\n autocomplete=\"off\"\n fluid\n @keydown.stop.prevent=\"captureKeybinding\"\n />\n <Message v-if=\"existingKeybindingOnCombo\" severity=\"warn\">\n {{ $t('g.keybindingAlreadyExists') }}\n <Tag\n severity=\"secondary\"\n :value=\"existingKeybindingOnCombo.commandId\"\n />\n </Message>\n </div>\n <template #footer>\n <Button\n :variant=\"existingKeybindingOnCombo ? 'destructive' : 'primary'\"\n autofocus\n @click=\"saveKeybinding\"\n >\n <i\n :class=\"existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'\"\n />\n {{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}\n </Button>\n </template>\n </Dialog>\n <Button\n v-tooltip=\"$t('g.resetAllKeybindingsTooltip')\"\n class=\"mt-4 w-full\"\n variant=\"destructive-textonly\"\n @click=\"resetAllKeybindings\"\n >\n <i class=\"pi pi-replay\" />\n {{ $t('g.resetAll') }}\n </Button>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterMatchMode } from '@primevue/core/api'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Dialog from 'primevue/dialog'\nimport InputText from 'primevue/inputtext'\nimport Message from 'primevue/message'\nimport Tag from 'primevue/tag'\nimport { useToast } from 'primevue/usetoast'\nimport { computed, ref, watchEffect } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport SearchBox from '@/components/common/SearchBox.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useKeybindingService } from '@/services/keybindingService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport {\n KeyComboImpl,\n KeybindingImpl,\n useKeybindingStore\n} from '@/stores/keybindingStore'\nimport { normalizeI18nKey } from '@/utils/formatUtil'\n\nimport PanelTemplate from './PanelTemplate.vue'\nimport KeyComboDisplay from './keybinding/KeyComboDisplay.vue'\n\nconst filters = ref({\n global: { value: '', matchMode: FilterMatchMode.CONTAINS }\n})\n\nconst keybindingStore = useKeybindingStore()\nconst keybindingService = useKeybindingService()\nconst commandStore = useCommandStore()\nconst { t } = useI18n()\n\ninterface ICommandData {\n id: string\n keybinding: KeybindingImpl | null\n label: string\n source?: string\n}\n\nconst commandsData = computed<ICommandData[]>(() => {\n return Object.values(commandStore.commands).map((command) => ({\n id: command.id,\n label: t(\n `commands.${normalizeI18nKey(command.id)}.label`,\n command.label ?? ''\n ),\n keybinding: keybindingStore.getKeybindingByCommandId(command.id),\n source: command.source\n }))\n})\n\nconst selectedCommandData = ref<ICommandData | null>(null)\nconst editDialogVisible = ref(false)\nconst newBindingKeyCombo = ref<KeyComboImpl | null>(null)\nconst currentEditingCommand = ref<ICommandData | null>(null)\nconst keybindingInput = ref<InstanceType<typeof InputText> | null>(null)\n\nconst existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {\n if (!currentEditingCommand.value) {\n return null\n }\n\n // If the new keybinding is the same as the current editing command, then don't show the error\n if (\n currentEditingCommand.value.keybinding?.combo?.equals(\n newBindingKeyCombo.value\n )\n ) {\n return null\n }\n\n if (!newBindingKeyCombo.value) {\n return null\n }\n\n return keybindingStore.getKeybinding(newBindingKeyCombo.value)\n})\n\nfunction editKeybinding(commandData: ICommandData) {\n currentEditingCommand.value = commandData\n newBindingKeyCombo.value = commandData.keybinding\n ? commandData.keybinding.combo\n : null\n editDialogVisible.value = true\n}\n\nwatchEffect(() => {\n if (editDialogVisible.value) {\n // nextTick doesn't work here, so we use a timeout instead\n setTimeout(() => {\n // @ts-expect-error - $el is an internal property of the InputText component\n keybindingInput.value?.$el?.focus()\n }, 300)\n }\n})\n\nasync function removeKeybinding(commandData: ICommandData) {\n if (commandData.keybinding) {\n keybindingStore.unsetKeybinding(commandData.keybinding)\n await keybindingService.persistUserKeybindings()\n }\n}\n\nasync function captureKeybinding(event: KeyboardEvent) {\n // Allow the use of keyboard shortcuts when adding keyboard shortcuts\n if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Escape':\n cancelEdit()\n return\n case 'Enter':\n await saveKeybinding()\n return\n }\n }\n const keyCombo = KeyComboImpl.fromEvent(event)\n newBindingKeyCombo.value = keyCombo\n}\n\nfunction cancelEdit() {\n editDialogVisible.value = false\n currentEditingCommand.value = null\n newBindingKeyCombo.value = null\n}\n\nasync function saveKeybinding() {\n if (currentEditingCommand.value && newBindingKeyCombo.value) {\n const updated = keybindingStore.updateKeybindingOnCommand(\n new KeybindingImpl({\n commandId: currentEditingCommand.value.id,\n combo: newBindingKeyCombo.value\n })\n )\n if (updated) {\n await keybindingService.persistUserKeybindings()\n }\n }\n cancelEdit()\n}\n\nasync function resetKeybinding(commandData: ICommandData) {\n if (keybindingStore.resetKeybindingForCommand(commandData.id)) {\n await keybindingService.persistUserKeybindings()\n } else {\n console.warn(\n `No changes made when resetting keybinding for command: ${commandData.id}`\n )\n }\n}\n\nconst toast = useToast()\nasync function resetAllKeybindings() {\n keybindingStore.resetAllKeybindings()\n await keybindingService.persistUserKeybindings()\n toast.add({\n severity: 'info',\n summary: 'Info',\n detail: 'All keybindings reset',\n life: 3000\n })\n}\n</script>\n\n<style scoped>\n@reference '../../../../assets/css/style.css';\n\n:deep(.p-datatable-tbody) > tr > td {\n @apply p-1;\n min-height: 2rem;\n}\n\n:deep(.p-datatable-row-selected) .actions,\n:deep(.p-datatable-selectable-row:hover) .actions {\n @apply visible;\n}\n</style>\n","<template>\n <PanelTemplate value=\"Keybinding\" class=\"keybinding-panel\">\n <template #header>\n <SearchBox\n v-model=\"filters['global'].value\"\n :placeholder=\"$t('g.searchKeybindings') + '...'\"\n />\n </template>\n\n <DataTable\n v-model:selection=\"selectedCommandData\"\n :value=\"commandsData\"\n :global-filter-fields=\"['id', 'label']\"\n :filters=\"filters\"\n selection-mode=\"single\"\n striped-rows\n :pt=\"{\n header: 'px-0'\n }\"\n @row-dblclick=\"editKeybinding($event.data)\"\n >\n <Column field=\"actions\" header=\"\">\n <template #body=\"slotProps\">\n <div class=\"actions invisible flex flex-row\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.edit')\"\n @click=\"editKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-pencil\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.reset')\"\n :disabled=\"\n !keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n @click=\"resetKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-replay\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.delete')\"\n :disabled=\"!slotProps.data.keybinding\"\n @click=\"removeKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-trash\" />\n </Button>\n </div>\n </template>\n </Column>\n <Column\n field=\"id\"\n :header=\"$t('g.command')\"\n sortable\n class=\"max-w-64 2xl:max-w-full\"\n >\n <template #body=\"slotProps\">\n <div class=\"truncate\" :title=\"slotProps.data.id\">\n {{ slotProps.data.label }}\n </div>\n </template>\n </Column>\n <Column field=\"keybinding\" :header=\"$t('g.keybinding')\">\n <template #body=\"slotProps\">\n <KeyComboDisplay\n v-if=\"slotProps.data.keybinding\"\n :key-combo=\"slotProps.data.keybinding.combo\"\n :is-modified=\"\n keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n />\n <span v-else>-</span>\n </template>\n </Column>\n <Column field=\"source\" :header=\"$t('g.source')\">\n <template #body=\"slotProps\">\n <span class=\"overflow-hidden text-ellipsis\">{{\n slotProps.data.source || '-'\n }}</span>\n </template>\n </Column>\n </DataTable>\n\n <Dialog\n v-model:visible=\"editDialogVisible\"\n class=\"min-w-96\"\n modal\n :header=\"currentEditingCommand?.label\"\n @hide=\"cancelEdit\"\n >\n <div>\n <InputText\n ref=\"keybindingInput\"\n class=\"mb-2 text-center\"\n :model-value=\"newBindingKeyCombo?.toString() ?? ''\"\n :placeholder=\"$t('g.pressKeysForNewBinding')\"\n autocomplete=\"off\"\n fluid\n @keydown.stop.prevent=\"captureKeybinding\"\n />\n <Message v-if=\"existingKeybindingOnCombo\" severity=\"warn\">\n {{ $t('g.keybindingAlreadyExists') }}\n <Tag\n severity=\"secondary\"\n :value=\"existingKeybindingOnCombo.commandId\"\n />\n </Message>\n </div>\n <template #footer>\n <Button\n :variant=\"existingKeybindingOnCombo ? 'destructive' : 'primary'\"\n autofocus\n @click=\"saveKeybinding\"\n >\n <i\n :class=\"existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'\"\n />\n {{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}\n </Button>\n </template>\n </Dialog>\n <Button\n v-tooltip=\"$t('g.resetAllKeybindingsTooltip')\"\n class=\"mt-4 w-full\"\n variant=\"destructive-textonly\"\n @click=\"resetAllKeybindings\"\n >\n <i class=\"pi pi-replay\" />\n {{ $t('g.resetAll') }}\n </Button>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterMatchMode } from '@primevue/core/api'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Dialog from 'primevue/dialog'\nimport InputText from 'primevue/inputtext'\nimport Message from 'primevue/message'\nimport Tag from 'primevue/tag'\nimport { useToast } from 'primevue/usetoast'\nimport { computed, ref, watchEffect } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport SearchBox from '@/components/common/SearchBox.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useKeybindingService } from '@/services/keybindingService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport {\n KeyComboImpl,\n KeybindingImpl,\n useKeybindingStore\n} from '@/stores/keybindingStore'\nimport { normalizeI18nKey } from '@/utils/formatUtil'\n\nimport PanelTemplate from './PanelTemplate.vue'\nimport KeyComboDisplay from './keybinding/KeyComboDisplay.vue'\n\nconst filters = ref({\n global: { value: '', matchMode: FilterMatchMode.CONTAINS }\n})\n\nconst keybindingStore = useKeybindingStore()\nconst keybindingService = useKeybindingService()\nconst commandStore = useCommandStore()\nconst { t } = useI18n()\n\ninterface ICommandData {\n id: string\n keybinding: KeybindingImpl | null\n label: string\n source?: string\n}\n\nconst commandsData = computed<ICommandData[]>(() => {\n return Object.values(commandStore.commands).map((command) => ({\n id: command.id,\n label: t(\n `commands.${normalizeI18nKey(command.id)}.label`,\n command.label ?? ''\n ),\n keybinding: keybindingStore.getKeybindingByCommandId(command.id),\n source: command.source\n }))\n})\n\nconst selectedCommandData = ref<ICommandData | null>(null)\nconst editDialogVisible = ref(false)\nconst newBindingKeyCombo = ref<KeyComboImpl | null>(null)\nconst currentEditingCommand = ref<ICommandData | null>(null)\nconst keybindingInput = ref<InstanceType<typeof InputText> | null>(null)\n\nconst existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {\n if (!currentEditingCommand.value) {\n return null\n }\n\n // If the new keybinding is the same as the current editing command, then don't show the error\n if (\n currentEditingCommand.value.keybinding?.combo?.equals(\n newBindingKeyCombo.value\n )\n ) {\n return null\n }\n\n if (!newBindingKeyCombo.value) {\n return null\n }\n\n return keybindingStore.getKeybinding(newBindingKeyCombo.value)\n})\n\nfunction editKeybinding(commandData: ICommandData) {\n currentEditingCommand.value = commandData\n newBindingKeyCombo.value = commandData.keybinding\n ? commandData.keybinding.combo\n : null\n editDialogVisible.value = true\n}\n\nwatchEffect(() => {\n if (editDialogVisible.value) {\n // nextTick doesn't work here, so we use a timeout instead\n setTimeout(() => {\n // @ts-expect-error - $el is an internal property of the InputText component\n keybindingInput.value?.$el?.focus()\n }, 300)\n }\n})\n\nasync function removeKeybinding(commandData: ICommandData) {\n if (commandData.keybinding) {\n keybindingStore.unsetKeybinding(commandData.keybinding)\n await keybindingService.persistUserKeybindings()\n }\n}\n\nasync function captureKeybinding(event: KeyboardEvent) {\n // Allow the use of keyboard shortcuts when adding keyboard shortcuts\n if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Escape':\n cancelEdit()\n return\n case 'Enter':\n await saveKeybinding()\n return\n }\n }\n const keyCombo = KeyComboImpl.fromEvent(event)\n newBindingKeyCombo.value = keyCombo\n}\n\nfunction cancelEdit() {\n editDialogVisible.value = false\n currentEditingCommand.value = null\n newBindingKeyCombo.value = null\n}\n\nasync function saveKeybinding() {\n if (currentEditingCommand.value && newBindingKeyCombo.value) {\n const updated = keybindingStore.updateKeybindingOnCommand(\n new KeybindingImpl({\n commandId: currentEditingCommand.value.id,\n combo: newBindingKeyCombo.value\n })\n )\n if (updated) {\n await keybindingService.persistUserKeybindings()\n }\n }\n cancelEdit()\n}\n\nasync function resetKeybinding(commandData: ICommandData) {\n if (keybindingStore.resetKeybindingForCommand(commandData.id)) {\n await keybindingService.persistUserKeybindings()\n } else {\n console.warn(\n `No changes made when resetting keybinding for command: ${commandData.id}`\n )\n }\n}\n\nconst toast = useToast()\nasync function resetAllKeybindings() {\n keybindingStore.resetAllKeybindings()\n await keybindingService.persistUserKeybindings()\n toast.add({\n severity: 'info',\n summary: 'Info',\n detail: 'All keybindings reset',\n life: 3000\n })\n}\n</script>\n\n<style scoped>\n@reference '../../../../assets/css/style.css';\n\n:deep(.p-datatable-tbody) > tr > td {\n @apply p-1;\n min-height: 2rem;\n}\n\n:deep(.p-datatable-row-selected) .actions,\n:deep(.p-datatable-selectable-row:hover) .actions {\n @apply visible;\n}\n</style>\n"],"mappings":"0oCCsBA,MAAM,EAAe,EAAA,IAAe,EAAA,SAAS,gBAAA,CAAiB,iaE8I9D,MAAM,EAAU,EAAI,CAClB,OAAQ,CAAE,MAAO,GAAI,UAAW,EAAgB,SAAS,CAC1D,EAEK,EAAkB,GAAA,EAClB,EAAoB,GAAA,EACpB,EAAe,GAAA,EACf,CAAE,EAAA,CAAA,EAAM,GAAA,EASR,EAAe,EAAA,IACZ,OAAO,OAAO,EAAa,QAAA,EAAU,IAAK,IAAa,CAC5D,GAAI,EAAQ,GACZ,MAAO,EACL,YAAY,GAAiB,EAAQ,EAAA,CAAG,SACxC,EAAQ,OAAS,EAAA,EAEnB,WAAY,EAAgB,yBAAyB,EAAQ,EAAA,EAC7D,OAAQ,EAAQ,QACjB,GAGG,EAAsB,EAAyB,IAAA,EAC/C,EAAoB,EAAI,EAAA,EACxB,EAAqB,EAAyB,IAAA,EAC9C,EAAwB,EAAyB,IAAA,EACjD,EAAkB,EAA2C,IAAA,EAE7D,EAA4B,EAAA,IAC5B,CAAC,EAAsB,OAMzB,EAAsB,MAAM,YAAY,OAAO,OAC7C,EAAmB,KAAA,GAMnB,CAAC,EAAmB,MACf,KAGF,EAAgB,cAAc,EAAmB,KAAA,GAG1D,SAAS,EAAe,EAA2B,CACjD,EAAsB,MAAQ,EAC9B,EAAmB,MAAQ,EAAY,WACnC,EAAY,WAAW,MACvB,KACJ,EAAkB,MAAQ,GALnB,EAAA,EAAA,kBAQT,EAAA,IAAkB,CACZ,EAAkB,OAEpB,WAAA,IAAiB,CAEf,EAAgB,OAAO,KAAK,MAAA,GAC3B,GAAA,IAIP,eAAe,EAAiB,EAA2B,CACrD,EAAY,aACd,EAAgB,gBAAgB,EAAY,UAAA,EAC5C,MAAM,EAAkB,uBAAA,GAHb,EAAA,EAAA,oBAOf,eAAe,EAAkB,EAAsB,CAErD,GAAI,CAAC,EAAM,UAAY,CAAC,EAAM,QAAU,CAAC,EAAM,SAAW,CAAC,EAAM,QAC/D,OAAQ,EAAM,IAAd,CACE,IAAK,SACH,EAAA,EACA,OACF,IAAK,QACH,MAAM,EAAA,EACN,OAIN,EAAmB,MADF,GAAa,UAAU,CAAA,EAZ3B,EAAA,EAAA,qBAgBf,SAAS,GAAa,CACpB,EAAkB,MAAQ,GAC1B,EAAsB,MAAQ,KAC9B,EAAmB,MAAQ,KAHpB,EAAA,EAAA,cAMT,eAAe,GAAiB,CAC1B,EAAsB,OAAS,EAAmB,OACpC,EAAgB,0BAC9B,IAAI,GAAe,CACjB,UAAW,EAAsB,MAAM,GACvC,MAAO,EAAmB,MAC3B,CAAA,GAGD,MAAM,EAAkB,uBAAA,EAG5B,EAAA,EAZa,EAAA,EAAA,kBAef,eAAe,EAAgB,EAA2B,CACpD,EAAgB,0BAA0B,EAAY,EAAA,EACxD,MAAM,EAAkB,uBAAA,EAExB,QAAQ,KACN,0DAA0D,EAAY,EAAA,EAAA,EAL7D,EAAA,EAAA,mBAUf,MAAM,EAAQ,EAAA,EACd,eAAe,GAAsB,CACnC,EAAgB,oBAAA,EAChB,MAAM,EAAkB,uBAAA,EACxB,EAAM,IAAI,CACR,SAAU,OACV,QAAS,OACT,OAAQ,wBACR,KAAM,IACP,EARY,OAAA,EAAA,EAAA"}
|
|
1
|
+
{"version":3,"file":"KeybindingPanel-DF-bG4iO.js","names":[],"sources":["../../src/components/dialog/content/setting/keybinding/KeyComboDisplay.vue","../../src/components/dialog/content/setting/keybinding/KeyComboDisplay.vue","../../src/components/dialog/content/setting/KeybindingPanel.vue","../../src/components/dialog/content/setting/KeybindingPanel.vue"],"sourcesContent":["<template>\n <span>\n <template v-for=\"(sequence, index) in keySequences\" :key=\"index\">\n <Tag :severity=\"isModified ? 'info' : 'secondary'\">\n {{ sequence }}\n </Tag>\n <span v-if=\"index < keySequences.length - 1\" class=\"px-2\">+</span>\n </template>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\n\nimport type { KeyComboImpl } from '@/stores/keybindingStore'\n\nconst { keyCombo, isModified = false } = defineProps<{\n keyCombo: KeyComboImpl\n isModified?: boolean\n}>()\n\nconst keySequences = computed(() => keyCombo.getKeySequences())\n</script>\n","<template>\n <span>\n <template v-for=\"(sequence, index) in keySequences\" :key=\"index\">\n <Tag :severity=\"isModified ? 'info' : 'secondary'\">\n {{ sequence }}\n </Tag>\n <span v-if=\"index < keySequences.length - 1\" class=\"px-2\">+</span>\n </template>\n </span>\n</template>\n\n<script setup lang=\"ts\">\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\n\nimport type { KeyComboImpl } from '@/stores/keybindingStore'\n\nconst { keyCombo, isModified = false } = defineProps<{\n keyCombo: KeyComboImpl\n isModified?: boolean\n}>()\n\nconst keySequences = computed(() => keyCombo.getKeySequences())\n</script>\n","<template>\n <PanelTemplate value=\"Keybinding\" class=\"keybinding-panel\">\n <template #header>\n <SearchBox\n v-model=\"filters['global'].value\"\n :placeholder=\"$t('g.searchKeybindings') + '...'\"\n />\n </template>\n\n <DataTable\n v-model:selection=\"selectedCommandData\"\n :value=\"commandsData\"\n :global-filter-fields=\"['id', 'label']\"\n :filters=\"filters\"\n selection-mode=\"single\"\n striped-rows\n :pt=\"{\n header: 'px-0'\n }\"\n @row-dblclick=\"editKeybinding($event.data)\"\n >\n <Column field=\"actions\" header=\"\">\n <template #body=\"slotProps\">\n <div class=\"actions invisible flex flex-row\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.edit')\"\n @click=\"editKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-pencil\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.reset')\"\n :disabled=\"\n !keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n @click=\"resetKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-replay\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.delete')\"\n :disabled=\"!slotProps.data.keybinding\"\n @click=\"removeKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-trash\" />\n </Button>\n </div>\n </template>\n </Column>\n <Column\n field=\"id\"\n :header=\"$t('g.command')\"\n sortable\n class=\"max-w-64 2xl:max-w-full\"\n >\n <template #body=\"slotProps\">\n <div class=\"truncate\" :title=\"slotProps.data.id\">\n {{ slotProps.data.label }}\n </div>\n </template>\n </Column>\n <Column field=\"keybinding\" :header=\"$t('g.keybinding')\">\n <template #body=\"slotProps\">\n <KeyComboDisplay\n v-if=\"slotProps.data.keybinding\"\n :key-combo=\"slotProps.data.keybinding.combo\"\n :is-modified=\"\n keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n />\n <span v-else>-</span>\n </template>\n </Column>\n <Column field=\"source\" :header=\"$t('g.source')\">\n <template #body=\"slotProps\">\n <span class=\"overflow-hidden text-ellipsis\">{{\n slotProps.data.source || '-'\n }}</span>\n </template>\n </Column>\n </DataTable>\n\n <Dialog\n v-model:visible=\"editDialogVisible\"\n class=\"min-w-96\"\n modal\n :header=\"currentEditingCommand?.label\"\n @hide=\"cancelEdit\"\n >\n <div>\n <InputText\n ref=\"keybindingInput\"\n class=\"mb-2 text-center\"\n :model-value=\"newBindingKeyCombo?.toString() ?? ''\"\n :placeholder=\"$t('g.pressKeysForNewBinding')\"\n autocomplete=\"off\"\n fluid\n @keydown.stop.prevent=\"captureKeybinding\"\n />\n <Message v-if=\"existingKeybindingOnCombo\" severity=\"warn\">\n {{ $t('g.keybindingAlreadyExists') }}\n <Tag\n severity=\"secondary\"\n :value=\"existingKeybindingOnCombo.commandId\"\n />\n </Message>\n </div>\n <template #footer>\n <Button\n :variant=\"existingKeybindingOnCombo ? 'destructive' : 'primary'\"\n autofocus\n @click=\"saveKeybinding\"\n >\n <i\n :class=\"existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'\"\n />\n {{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}\n </Button>\n </template>\n </Dialog>\n <Button\n v-tooltip=\"$t('g.resetAllKeybindingsTooltip')\"\n class=\"mt-4 w-full\"\n variant=\"destructive-textonly\"\n @click=\"resetAllKeybindings\"\n >\n <i class=\"pi pi-replay\" />\n {{ $t('g.resetAll') }}\n </Button>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterMatchMode } from '@primevue/core/api'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Dialog from 'primevue/dialog'\nimport InputText from 'primevue/inputtext'\nimport Message from 'primevue/message'\nimport Tag from 'primevue/tag'\nimport { useToast } from 'primevue/usetoast'\nimport { computed, ref, watchEffect } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport SearchBox from '@/components/common/SearchBox.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useKeybindingService } from '@/services/keybindingService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport {\n KeyComboImpl,\n KeybindingImpl,\n useKeybindingStore\n} from '@/stores/keybindingStore'\nimport { normalizeI18nKey } from '@/utils/formatUtil'\n\nimport PanelTemplate from './PanelTemplate.vue'\nimport KeyComboDisplay from './keybinding/KeyComboDisplay.vue'\n\nconst filters = ref({\n global: { value: '', matchMode: FilterMatchMode.CONTAINS }\n})\n\nconst keybindingStore = useKeybindingStore()\nconst keybindingService = useKeybindingService()\nconst commandStore = useCommandStore()\nconst { t } = useI18n()\n\ninterface ICommandData {\n id: string\n keybinding: KeybindingImpl | null\n label: string\n source?: string\n}\n\nconst commandsData = computed<ICommandData[]>(() => {\n return Object.values(commandStore.commands).map((command) => ({\n id: command.id,\n label: t(\n `commands.${normalizeI18nKey(command.id)}.label`,\n command.label ?? ''\n ),\n keybinding: keybindingStore.getKeybindingByCommandId(command.id),\n source: command.source\n }))\n})\n\nconst selectedCommandData = ref<ICommandData | null>(null)\nconst editDialogVisible = ref(false)\nconst newBindingKeyCombo = ref<KeyComboImpl | null>(null)\nconst currentEditingCommand = ref<ICommandData | null>(null)\nconst keybindingInput = ref<InstanceType<typeof InputText> | null>(null)\n\nconst existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {\n if (!currentEditingCommand.value) {\n return null\n }\n\n // If the new keybinding is the same as the current editing command, then don't show the error\n if (\n currentEditingCommand.value.keybinding?.combo?.equals(\n newBindingKeyCombo.value\n )\n ) {\n return null\n }\n\n if (!newBindingKeyCombo.value) {\n return null\n }\n\n return keybindingStore.getKeybinding(newBindingKeyCombo.value)\n})\n\nfunction editKeybinding(commandData: ICommandData) {\n currentEditingCommand.value = commandData\n newBindingKeyCombo.value = commandData.keybinding\n ? commandData.keybinding.combo\n : null\n editDialogVisible.value = true\n}\n\nwatchEffect(() => {\n if (editDialogVisible.value) {\n // nextTick doesn't work here, so we use a timeout instead\n setTimeout(() => {\n // @ts-expect-error - $el is an internal property of the InputText component\n keybindingInput.value?.$el?.focus()\n }, 300)\n }\n})\n\nasync function removeKeybinding(commandData: ICommandData) {\n if (commandData.keybinding) {\n keybindingStore.unsetKeybinding(commandData.keybinding)\n await keybindingService.persistUserKeybindings()\n }\n}\n\nasync function captureKeybinding(event: KeyboardEvent) {\n // Allow the use of keyboard shortcuts when adding keyboard shortcuts\n if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Escape':\n cancelEdit()\n return\n case 'Enter':\n await saveKeybinding()\n return\n }\n }\n const keyCombo = KeyComboImpl.fromEvent(event)\n newBindingKeyCombo.value = keyCombo\n}\n\nfunction cancelEdit() {\n editDialogVisible.value = false\n currentEditingCommand.value = null\n newBindingKeyCombo.value = null\n}\n\nasync function saveKeybinding() {\n if (currentEditingCommand.value && newBindingKeyCombo.value) {\n const updated = keybindingStore.updateKeybindingOnCommand(\n new KeybindingImpl({\n commandId: currentEditingCommand.value.id,\n combo: newBindingKeyCombo.value\n })\n )\n if (updated) {\n await keybindingService.persistUserKeybindings()\n }\n }\n cancelEdit()\n}\n\nasync function resetKeybinding(commandData: ICommandData) {\n if (keybindingStore.resetKeybindingForCommand(commandData.id)) {\n await keybindingService.persistUserKeybindings()\n } else {\n console.warn(\n `No changes made when resetting keybinding for command: ${commandData.id}`\n )\n }\n}\n\nconst toast = useToast()\nasync function resetAllKeybindings() {\n keybindingStore.resetAllKeybindings()\n await keybindingService.persistUserKeybindings()\n toast.add({\n severity: 'info',\n summary: 'Info',\n detail: 'All keybindings reset',\n life: 3000\n })\n}\n</script>\n\n<style scoped>\n@reference '../../../../assets/css/style.css';\n\n:deep(.p-datatable-tbody) > tr > td {\n @apply p-1;\n min-height: 2rem;\n}\n\n:deep(.p-datatable-row-selected) .actions,\n:deep(.p-datatable-selectable-row:hover) .actions {\n @apply visible;\n}\n</style>\n","<template>\n <PanelTemplate value=\"Keybinding\" class=\"keybinding-panel\">\n <template #header>\n <SearchBox\n v-model=\"filters['global'].value\"\n :placeholder=\"$t('g.searchKeybindings') + '...'\"\n />\n </template>\n\n <DataTable\n v-model:selection=\"selectedCommandData\"\n :value=\"commandsData\"\n :global-filter-fields=\"['id', 'label']\"\n :filters=\"filters\"\n selection-mode=\"single\"\n striped-rows\n :pt=\"{\n header: 'px-0'\n }\"\n @row-dblclick=\"editKeybinding($event.data)\"\n >\n <Column field=\"actions\" header=\"\">\n <template #body=\"slotProps\">\n <div class=\"actions invisible flex flex-row\">\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.edit')\"\n @click=\"editKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-pencil\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.reset')\"\n :disabled=\"\n !keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n @click=\"resetKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-replay\" />\n </Button>\n <Button\n variant=\"textonly\"\n size=\"icon\"\n :aria-label=\"$t('g.delete')\"\n :disabled=\"!slotProps.data.keybinding\"\n @click=\"removeKeybinding(slotProps.data)\"\n >\n <i class=\"pi pi-trash\" />\n </Button>\n </div>\n </template>\n </Column>\n <Column\n field=\"id\"\n :header=\"$t('g.command')\"\n sortable\n class=\"max-w-64 2xl:max-w-full\"\n >\n <template #body=\"slotProps\">\n <div class=\"truncate\" :title=\"slotProps.data.id\">\n {{ slotProps.data.label }}\n </div>\n </template>\n </Column>\n <Column field=\"keybinding\" :header=\"$t('g.keybinding')\">\n <template #body=\"slotProps\">\n <KeyComboDisplay\n v-if=\"slotProps.data.keybinding\"\n :key-combo=\"slotProps.data.keybinding.combo\"\n :is-modified=\"\n keybindingStore.isCommandKeybindingModified(slotProps.data.id)\n \"\n />\n <span v-else>-</span>\n </template>\n </Column>\n <Column field=\"source\" :header=\"$t('g.source')\">\n <template #body=\"slotProps\">\n <span class=\"overflow-hidden text-ellipsis\">{{\n slotProps.data.source || '-'\n }}</span>\n </template>\n </Column>\n </DataTable>\n\n <Dialog\n v-model:visible=\"editDialogVisible\"\n class=\"min-w-96\"\n modal\n :header=\"currentEditingCommand?.label\"\n @hide=\"cancelEdit\"\n >\n <div>\n <InputText\n ref=\"keybindingInput\"\n class=\"mb-2 text-center\"\n :model-value=\"newBindingKeyCombo?.toString() ?? ''\"\n :placeholder=\"$t('g.pressKeysForNewBinding')\"\n autocomplete=\"off\"\n fluid\n @keydown.stop.prevent=\"captureKeybinding\"\n />\n <Message v-if=\"existingKeybindingOnCombo\" severity=\"warn\">\n {{ $t('g.keybindingAlreadyExists') }}\n <Tag\n severity=\"secondary\"\n :value=\"existingKeybindingOnCombo.commandId\"\n />\n </Message>\n </div>\n <template #footer>\n <Button\n :variant=\"existingKeybindingOnCombo ? 'destructive' : 'primary'\"\n autofocus\n @click=\"saveKeybinding\"\n >\n <i\n :class=\"existingKeybindingOnCombo ? 'pi pi-pencil' : 'pi pi-check'\"\n />\n {{ existingKeybindingOnCombo ? $t('g.overwrite') : $t('g.save') }}\n </Button>\n </template>\n </Dialog>\n <Button\n v-tooltip=\"$t('g.resetAllKeybindingsTooltip')\"\n class=\"mt-4 w-full\"\n variant=\"destructive-textonly\"\n @click=\"resetAllKeybindings\"\n >\n <i class=\"pi pi-replay\" />\n {{ $t('g.resetAll') }}\n </Button>\n </PanelTemplate>\n</template>\n\n<script setup lang=\"ts\">\nimport { FilterMatchMode } from '@primevue/core/api'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Dialog from 'primevue/dialog'\nimport InputText from 'primevue/inputtext'\nimport Message from 'primevue/message'\nimport Tag from 'primevue/tag'\nimport { useToast } from 'primevue/usetoast'\nimport { computed, ref, watchEffect } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport SearchBox from '@/components/common/SearchBox.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useKeybindingService } from '@/services/keybindingService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport {\n KeyComboImpl,\n KeybindingImpl,\n useKeybindingStore\n} from '@/stores/keybindingStore'\nimport { normalizeI18nKey } from '@/utils/formatUtil'\n\nimport PanelTemplate from './PanelTemplate.vue'\nimport KeyComboDisplay from './keybinding/KeyComboDisplay.vue'\n\nconst filters = ref({\n global: { value: '', matchMode: FilterMatchMode.CONTAINS }\n})\n\nconst keybindingStore = useKeybindingStore()\nconst keybindingService = useKeybindingService()\nconst commandStore = useCommandStore()\nconst { t } = useI18n()\n\ninterface ICommandData {\n id: string\n keybinding: KeybindingImpl | null\n label: string\n source?: string\n}\n\nconst commandsData = computed<ICommandData[]>(() => {\n return Object.values(commandStore.commands).map((command) => ({\n id: command.id,\n label: t(\n `commands.${normalizeI18nKey(command.id)}.label`,\n command.label ?? ''\n ),\n keybinding: keybindingStore.getKeybindingByCommandId(command.id),\n source: command.source\n }))\n})\n\nconst selectedCommandData = ref<ICommandData | null>(null)\nconst editDialogVisible = ref(false)\nconst newBindingKeyCombo = ref<KeyComboImpl | null>(null)\nconst currentEditingCommand = ref<ICommandData | null>(null)\nconst keybindingInput = ref<InstanceType<typeof InputText> | null>(null)\n\nconst existingKeybindingOnCombo = computed<KeybindingImpl | null>(() => {\n if (!currentEditingCommand.value) {\n return null\n }\n\n // If the new keybinding is the same as the current editing command, then don't show the error\n if (\n currentEditingCommand.value.keybinding?.combo?.equals(\n newBindingKeyCombo.value\n )\n ) {\n return null\n }\n\n if (!newBindingKeyCombo.value) {\n return null\n }\n\n return keybindingStore.getKeybinding(newBindingKeyCombo.value)\n})\n\nfunction editKeybinding(commandData: ICommandData) {\n currentEditingCommand.value = commandData\n newBindingKeyCombo.value = commandData.keybinding\n ? commandData.keybinding.combo\n : null\n editDialogVisible.value = true\n}\n\nwatchEffect(() => {\n if (editDialogVisible.value) {\n // nextTick doesn't work here, so we use a timeout instead\n setTimeout(() => {\n // @ts-expect-error - $el is an internal property of the InputText component\n keybindingInput.value?.$el?.focus()\n }, 300)\n }\n})\n\nasync function removeKeybinding(commandData: ICommandData) {\n if (commandData.keybinding) {\n keybindingStore.unsetKeybinding(commandData.keybinding)\n await keybindingService.persistUserKeybindings()\n }\n}\n\nasync function captureKeybinding(event: KeyboardEvent) {\n // Allow the use of keyboard shortcuts when adding keyboard shortcuts\n if (!event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) {\n switch (event.key) {\n case 'Escape':\n cancelEdit()\n return\n case 'Enter':\n await saveKeybinding()\n return\n }\n }\n const keyCombo = KeyComboImpl.fromEvent(event)\n newBindingKeyCombo.value = keyCombo\n}\n\nfunction cancelEdit() {\n editDialogVisible.value = false\n currentEditingCommand.value = null\n newBindingKeyCombo.value = null\n}\n\nasync function saveKeybinding() {\n if (currentEditingCommand.value && newBindingKeyCombo.value) {\n const updated = keybindingStore.updateKeybindingOnCommand(\n new KeybindingImpl({\n commandId: currentEditingCommand.value.id,\n combo: newBindingKeyCombo.value\n })\n )\n if (updated) {\n await keybindingService.persistUserKeybindings()\n }\n }\n cancelEdit()\n}\n\nasync function resetKeybinding(commandData: ICommandData) {\n if (keybindingStore.resetKeybindingForCommand(commandData.id)) {\n await keybindingService.persistUserKeybindings()\n } else {\n console.warn(\n `No changes made when resetting keybinding for command: ${commandData.id}`\n )\n }\n}\n\nconst toast = useToast()\nasync function resetAllKeybindings() {\n keybindingStore.resetAllKeybindings()\n await keybindingService.persistUserKeybindings()\n toast.add({\n severity: 'info',\n summary: 'Info',\n detail: 'All keybindings reset',\n life: 3000\n })\n}\n</script>\n\n<style scoped>\n@reference '../../../../assets/css/style.css';\n\n:deep(.p-datatable-tbody) > tr > td {\n @apply p-1;\n min-height: 2rem;\n}\n\n:deep(.p-datatable-row-selected) .actions,\n:deep(.p-datatable-selectable-row:hover) .actions {\n @apply visible;\n}\n</style>\n"],"mappings":"0oCCsBA,MAAM,EAAe,EAAA,IAAe,EAAA,SAAS,gBAAA,CAAiB,iaE8I9D,MAAM,EAAU,EAAI,CAClB,OAAQ,CAAE,MAAO,GAAI,UAAW,EAAgB,SAAS,CAC1D,EAEK,EAAkB,GAAA,EAClB,EAAoB,GAAA,EACpB,EAAe,GAAA,EACf,CAAE,EAAA,CAAA,EAAM,GAAA,EASR,EAAe,EAAA,IACZ,OAAO,OAAO,EAAa,QAAA,EAAU,IAAK,IAAa,CAC5D,GAAI,EAAQ,GACZ,MAAO,EACL,YAAY,GAAiB,EAAQ,EAAA,CAAG,SACxC,EAAQ,OAAS,EAAA,EAEnB,WAAY,EAAgB,yBAAyB,EAAQ,EAAA,EAC7D,OAAQ,EAAQ,QACjB,GAGG,EAAsB,EAAyB,IAAA,EAC/C,EAAoB,EAAI,EAAA,EACxB,EAAqB,EAAyB,IAAA,EAC9C,EAAwB,EAAyB,IAAA,EACjD,EAAkB,EAA2C,IAAA,EAE7D,EAA4B,EAAA,IAC5B,CAAC,EAAsB,OAMzB,EAAsB,MAAM,YAAY,OAAO,OAC7C,EAAmB,KAAA,GAMnB,CAAC,EAAmB,MACf,KAGF,EAAgB,cAAc,EAAmB,KAAA,GAG1D,SAAS,EAAe,EAA2B,CACjD,EAAsB,MAAQ,EAC9B,EAAmB,MAAQ,EAAY,WACnC,EAAY,WAAW,MACvB,KACJ,EAAkB,MAAQ,GALnB,EAAA,EAAA,kBAQT,EAAA,IAAkB,CACZ,EAAkB,OAEpB,WAAA,IAAiB,CAEf,EAAgB,OAAO,KAAK,MAAA,GAC3B,GAAA,IAIP,eAAe,EAAiB,EAA2B,CACrD,EAAY,aACd,EAAgB,gBAAgB,EAAY,UAAA,EAC5C,MAAM,EAAkB,uBAAA,GAHb,EAAA,EAAA,oBAOf,eAAe,EAAkB,EAAsB,CAErD,GAAI,CAAC,EAAM,UAAY,CAAC,EAAM,QAAU,CAAC,EAAM,SAAW,CAAC,EAAM,QAC/D,OAAQ,EAAM,IAAd,CACE,IAAK,SACH,EAAA,EACA,OACF,IAAK,QACH,MAAM,EAAA,EACN,OAIN,EAAmB,MADF,GAAa,UAAU,CAAA,EAZ3B,EAAA,EAAA,qBAgBf,SAAS,GAAa,CACpB,EAAkB,MAAQ,GAC1B,EAAsB,MAAQ,KAC9B,EAAmB,MAAQ,KAHpB,EAAA,EAAA,cAMT,eAAe,GAAiB,CAC1B,EAAsB,OAAS,EAAmB,OACpC,EAAgB,0BAC9B,IAAI,GAAe,CACjB,UAAW,EAAsB,MAAM,GACvC,MAAO,EAAmB,MAC3B,CAAA,GAGD,MAAM,EAAkB,uBAAA,EAG5B,EAAA,EAZa,EAAA,EAAA,kBAef,eAAe,EAAgB,EAA2B,CACpD,EAAgB,0BAA0B,EAAY,EAAA,EACxD,MAAM,EAAkB,uBAAA,EAExB,QAAQ,KACN,0DAA0D,EAAY,EAAA,EAAA,EAL7D,EAAA,EAAA,mBAUf,MAAM,EAAQ,EAAA,EACd,eAAe,GAAsB,CACnC,EAAgB,oBAAA,EAChB,MAAM,EAAkB,uBAAA,EACxB,EAAM,IAAI,CACR,SAAU,OACV,QAAS,OACT,OAAQ,wBACR,KAAM,IACP,EARY,OAAA,EAAA,EAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var o=(f,u)=>Z(f,"name",{value:u,configurable:!0});import{L as B,N as V,X as H,Z as Q,c as W,f as ee,lt as F,nt as te,ut as ae}from"./vendor-primevue-DcMRXJN3.js";import{$o as i,Ba as r,Do as c,Ha as I,Jo as a,Ka as T,Oo as se,Ua as x,Va as U,Xo as G,Ya as j,di as J,do as p,go as re,qa as n,wo as K,za as D,zo as E}from"./vendor-other-DlQF6V2E.js";import{n as X}from"./vendor-vue-D9IUuwPJ.js";import{nt as ie}from"./api-CUAc7rDA.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-CI8_F4yx.js";import{t as S}from"./Button-Do2I1OAA.js";import{c as oe,k as ne}from"./PanelTemplate-BjN5XNg2.js";import{Hr as Y,Mr as le,Nr as de,Or as ce,Vr as q,ft as R,o as M,t as ue}from"./dialogService-BZ1FmjZL.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BkgQPjq6.js";var pe={key:0,class:"flex items-center gap-1"},me={class:"flex items-center gap-2"},ve={key:1,class:"flex items-center gap-1"},fe=j({__name:"UserCredit",props:{textClass:{},showCreditsOnly:{type:Boolean}},setup(f){const u=q(),k=D(()=>u.isFetchingBalance),{t:w,locale:m}=X(),v=D(()=>`${M({cents:u.balance?.effective_balance_micros??u.balance?.amount_micros??0,locale:m.value})} ${w("credits.credits")}`),d=D(()=>M({cents:u.balance?.effective_balance_micros??u.balance?.amount_micros??0,locale:m.value,numberOptions:{minimumFractionDigits:0,maximumFractionDigits:0}}));return($,C)=>k.value?(p(),x("div",pe,[r("div",me,[n(a(F),{shape:"circle",width:"1.5rem",height:"1.5rem"})]),C[0]||(C[0]=r("div",{class:"flex-1"},null,-1)),n(a(F),{width:"8rem",height:"2rem"})])):(p(),x("div",ve,[$.showCreditsOnly?I("",!0):(p(),U(a(W),{key:0,severity:"secondary",rounded:"",class:"p-1 text-amber-400"},{icon:c(()=>C[1]||(C[1]=[r("i",{class:"icon-[lucide--component]"},null,-1)])),_:1})),r("div",{class:G($.textClass)},i($.showCreditsOnly?d.value:v.value),3)]))}}),_e=fe;let N=(function(f){return f.CREDIT_ADDED="credit_added",f.ACCOUNT_CREATED="account_created",f.API_USAGE_STARTED="api_usage_started",f.API_USAGE_COMPLETED="api_usage_completed",f})({});var z=J.create({baseURL:Y(),headers:{"Content-Type":"application/json"}});const ge=o(()=>{const f=E(!1),u=E(null),{d:k}=X();K(()=>Y(),e=>{z.defaults.baseURL=e});const w=o((e,s,b)=>{if(ie(e))return;let h;if(!J.isAxiosError(e))h=`${s} failed: ${e instanceof Error?e.message:String(e)}`;else{const y=e,l=y.response?.status;l&&b?.[l]?h=b[l]:h=y.response?.data?.message??`${s} failed with status ${l}`}u.value=h},"handleRequestError"),m=o(async(e,s)=>{const{errorContext:b,routeSpecificErrors:h}=s;f.value=!0,u.value=null;try{return(await e()).data}catch(y){return w(y,b,h),null}finally{f.value=!1}},"executeRequest");function v(e){switch(e){case"credit_added":return"Credits Added";case"account_created":return"Account Created";case"api_usage_completed":return"API Usage";default:return e}}o(v,"formatEventType");function d(e){return k(new Date(e),{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}o(d,"formatDate");function $(e){return e.split("_").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join(" ")}o($,"formatJsonKey");function C(e){return typeof e=="number"?e.toLocaleString():typeof e=="string"&&e.match(/^\d{4}-\d{2}-\d{2}/)?new Date(e).toLocaleString():e}o(C,"formatJsonValue");function A(e){switch(e){case"credit_added":return"success";case"account_created":return"info";case"api_usage_completed":return"warning";default:return"info"}}o(A,"getEventSeverity");function O(e){const{amount:s,api_name:b,model:h,...y}=e.params||{};return Object.keys(y).length>0}o(O,"hasAdditionalInfo");function P(e){const{...s}=e.params||{};return Object.entries(s).map(([b,h])=>`<strong>${$(b)}:</strong> ${C(h)}`).join("<br>")}o(P,"getTooltipContent");function t(e){return e?(e/100).toFixed(2):"0.00"}o(t,"formatAmount");async function g({page:e=1,limit:s=10}={}){const b="Fetching customer events",h={400:"Invalid input, object invalid",404:"Not found"},y=await q().getAuthHeader();return y?await m(()=>z.get("/customers/events",{params:{page:e,limit:s},headers:y}),{errorContext:b,routeSpecificErrors:h}):(u.value="Authentication header is missing",null)}return o(g,"getMyEvents"),{isLoading:f,error:u,getMyEvents:g,formatEventType:v,getEventSeverity:A,formatAmount:t,hasAdditionalInfo:O,formatDate:d,formatJsonKey:$,formatJsonValue:C,getTooltipContent:P}},"useCustomerEventsService");var he={key:0,class:"flex items-center justify-center p-8"},ye={key:1,class:"p-4"},Ce={class:"event-details"},be={key:0,class:"font-semibold text-green-500"},$e={key:1},xe={key:2,class:"flex flex-col gap-1"},ke={class:"font-semibold"},we={class:"text-sm text-smoke-400"},Ae=j({__name:"UsageLogsTable",setup(f,{expose:u}){const k=E([]),w=E(!0),m=E(null),v=ge(),d=E({page:1,limit:7,total:0,totalPages:0}),$=D(()=>(d.value.page-1)*d.value.limit),C=D(()=>{const t=new Map;return k.value.forEach(g=>{v.hasAdditionalInfo(g)&&g.event_id&&t.set(g.event_id,v.getTooltipContent(g))}),t}),A=o(async()=>{w.value=!0,m.value=null;try{const t=await v.getMyEvents({page:d.value.page,limit:d.value.limit});t?(t.events&&(k.value=t.events),t.page&&(d.value.page=t.page),t.limit&&(d.value.limit=t.limit),t.total&&(d.value.total=t.total),t.totalPages&&(d.value.totalPages=t.totalPages),R()?.checkForCompletedTopup(t.events)):m.value=v.error.value||"Failed to load events"}catch(t){m.value=t instanceof Error?t.message:"Unknown error",console.error("Error loading events:",t)}finally{w.value=!1}},"loadEvents"),O=o(t=>{d.value.page=t.page+1,A().catch(g=>{console.error("Error loading events:",g)})},"onPageChange");return u({refresh:o(async()=>{d.value.page=1,await A()},"refresh")}),(t,g)=>{const e=re("tooltip");return p(),x("div",null,[w.value?(p(),x("div",he,[n(a(Q))])):m.value?(p(),x("div",ye,[n(a(ae),{severity:"error",closable:!1},{default:c(()=>[T(i(m.value),1)]),_:1})])):(p(),U(a(V),{key:2,value:k.value,paginator:!0,rows:d.value.limit,"total-records":d.value.total,first:$.value,lazy:!0,class:"p-datatable-sm custom-datatable",onPage:O},{default:c(()=>[n(a(B),{field:"event_type",header:t.$t("credits.eventType")},{body:c(({data:s})=>[n(a(te),{value:a(v).formatEventType(s.event_type),severity:a(v).getEventSeverity(s.event_type)},null,8,["value","severity"])]),_:1},8,["header"]),n(a(B),{field:"details",header:t.$t("credits.details")},{body:c(({data:s})=>[r("div",Ce,[s.event_type===a(N).CREDIT_ADDED?(p(),x("div",be,i(t.$t("credits.added"))+" $"+i(a(v).formatAmount(s.params?.amount)),1)):s.event_type===a(N).ACCOUNT_CREATED?(p(),x("div",$e,i(t.$t("credits.accountInitialized")),1)):s.event_type===a(N).API_USAGE_COMPLETED?(p(),x("div",xe,[r("div",ke,i(s.params?.api_name||"API"),1),r("div",we,i(t.$t("credits.model"))+": "+i(s.params?.model||"-"),1)])):I("",!0)])]),_:1},8,["header"]),n(a(B),{field:"createdAt",header:t.$t("credits.time")},{body:c(({data:s})=>[T(i(a(v).formatDate(s.createdAt)),1)]),_:1},8,["header"]),n(a(B),{field:"params",header:t.$t("credits.additionalInfo")},{body:c(({data:s})=>[a(v).hasAdditionalInfo(s)?se((p(),U(S,{key:0,variant:"textonly",size:"icon-sm","aria-label":t.$t("credits.additionalInfo")},{default:c(()=>g[0]||(g[0]=[r("i",{class:"pi pi-info-circle"},null,-1)])),_:2},1032,["aria-label"])),[[e,{escape:!1,value:C.value.get(s.event_id)||"",pt:{text:{style:{width:"max-content !important"}}}},void 0,{top:!0}]]):I("",!0)]),_:1},8,["header"])]),_:1},8,["value","rows","total-records","first"]))])}}}),Ee=Ae,De={class:"flex h-full flex-col"},Se={class:"mb-2 text-2xl font-bold"},Te={class:"flex flex-col gap-2"},Ue={class:"text-sm font-medium text-muted"},Pe={class:"flex items-center justify-between"},Le={class:"flex flex-row items-center"},Be={key:1,class:"text-xs text-muted"},Ie={class:"flex items-center justify-between"},Oe={key:0,class:"grow"},Fe={class:"text-sm font-medium"},Ne={class:"text-xs text-muted"},Re={class:"flex flex-row gap-2"},je=j({__name:"LegacyCreditsPanel",setup(f){const{buildDocsUrl:u,docsPaths:k}=ne(),w=ue(),m=q(),v=le(),d=ce(),$=R(),{isActiveSubscription:C}=de(),A=D(()=>m.loading),O=D(()=>m.isFetchingBalance),P=E(null),t=D(()=>m.lastBalanceUpdateTime?m.lastBalanceUpdateTime.toLocaleString():"");K(()=>m.lastBalanceUpdateTime,(l,_)=>{l&&l!==_&&P.value&&P.value.refresh()});const g=o(()=>{R()?.trackAddApiCreditButtonClicked(),w.showTopUpCreditsDialog()},"handlePurchaseCreditsClick"),e=o(async()=>{await v.accessBillingPortal()},"handleCreditsHistoryClick"),s=o(async()=>{$?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"credits_panel"}),await d.execute("Comfy.ContactSupport")},"handleMessageSupport"),b=o(()=>{window.open(u("/tutorials/api-nodes/faq",{includeLocale:!0}),"_blank")},"handleFaqClick"),h=o(()=>{window.open(u(k.partnerNodesPricing,{includeLocale:!0}),"_blank")},"handleOpenPartnerNodesInfo"),y=E([]);return(l,_)=>(p(),U(a(ee),{value:"Credits",class:"credits-container h-full"},{default:c(()=>[r("div",De,[r("h2",Se,i(l.$t("credits.credits")),1),n(a(H)),r("div",Te,[r("h3",Ue,i(l.$t("credits.yourCreditBalance")),1),r("div",Pe,[n(_e,{"text-class":"text-3xl font-bold"}),A.value?(p(),U(a(F),{key:0,width:"2rem",height:"2rem"})):a(C)?(p(),U(S,{key:1,loading:A.value,onClick:g},{default:c(()=>[T(i(l.$t("credits.purchaseCredits")),1)]),_:1},8,["loading"])):I("",!0)]),r("div",Le,[O.value?(p(),U(a(F),{key:0,width:"12rem",height:"1rem",class:"text-xs"})):t.value?(p(),x("div",Be,i(l.$t("credits.lastUpdated"))+": "+i(t.value),1)):I("",!0),n(S,{variant:"muted-textonly",size:"icon-sm","aria-label":l.$t("g.refresh"),onClick:_[0]||(_[0]=()=>a(v).fetchBalance())},{default:c(()=>_[1]||(_[1]=[r("i",{class:"pi pi-refresh"},null,-1)])),_:1},8,["aria-label"])])]),r("div",Ie,[r("h3",null,i(l.$t("credits.activity")),1),n(S,{variant:"muted-textonly",loading:A.value,onClick:e},{default:c(()=>[_[2]||(_[2]=r("i",{class:"pi pi-arrow-up-right"},null,-1)),T(" "+i(l.$t("credits.invoiceHistory")),1)]),_:1},8,["loading"])]),y.value.length>0?(p(),x("div",Oe,[n(a(V),{value:y.value,"show-headers":!1},{default:c(()=>[n(a(B),{field:"title",header:l.$t("g.name")},{body:c(({data:L})=>[r("div",Fe,i(L.title),1),r("div",Ne,i(L.timestamp),1)]),_:1},8,["header"]),n(a(B),{field:"amount",header:l.$t("g.amount")},{body:c(({data:L})=>[r("div",{class:G(["text-center text-base font-medium",L.isPositive?"text-sky-500":"text-red-400"])},i(L.isPositive?"+":"-")+"$"+i(a(oe)(L.amount,"usd")),3)]),_:1},8,["header"])]),_:1},8,["value"])])):I("",!0),n(a(H)),n(Ee,{ref_key:"usageLogsTableRef",ref:P},null,512),r("div",Re,[n(S,{variant:"muted-textonly",onClick:b},{default:c(()=>[_[3]||(_[3]=r("i",{class:"pi pi-question-circle"},null,-1)),T(" "+i(l.$t("credits.faqs")),1)]),_:1}),n(S,{variant:"muted-textonly",onClick:h},{default:c(()=>[_[4]||(_[4]=r("i",{class:"pi pi-question-circle"},null,-1)),T(" "+i(l.$t("subscription.partnerNodesCredits")),1)]),_:1}),n(S,{variant:"muted-textonly",onClick:s},{default:c(()=>[_[5]||(_[5]=r("i",{class:"pi pi-comments"},null,-1)),T(" "+i(l.$t("credits.messageSupport")),1)]),_:1})])])]),_:1}))}}),st=je;export{st as default};
|
|
1
|
+
var Z=Object.defineProperty;var o=(f,u)=>Z(f,"name",{value:u,configurable:!0});import{L as B,N as V,X as H,Z as Q,c as W,f as ee,lt as F,nt as te,ut as ae}from"./vendor-primevue-DcMRXJN3.js";import{$o as i,Ba as r,Do as c,Ha as I,Jo as a,Ka as T,Oo as se,Ua as x,Va as U,Xo as G,Ya as j,di as J,do as p,go as re,qa as n,wo as K,za as D,zo as E}from"./vendor-other-DlQF6V2E.js";import{n as X}from"./vendor-vue-D9IUuwPJ.js";import{nt as ie}from"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-Cfa5N_7c.js";import{t as S}from"./Button-Do2I1OAA.js";import{c as oe,k as ne}from"./PanelTemplate-BJda9e5J.js";import{Hr as Y,Mr as le,Nr as de,Or as ce,Vr as q,ft as R,o as M,t as ue}from"./dialogService-YG0RH337.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BAS9m9W6.js";var pe={key:0,class:"flex items-center gap-1"},me={class:"flex items-center gap-2"},ve={key:1,class:"flex items-center gap-1"},fe=j({__name:"UserCredit",props:{textClass:{},showCreditsOnly:{type:Boolean}},setup(f){const u=q(),k=D(()=>u.isFetchingBalance),{t:w,locale:m}=X(),v=D(()=>`${M({cents:u.balance?.effective_balance_micros??u.balance?.amount_micros??0,locale:m.value})} ${w("credits.credits")}`),d=D(()=>M({cents:u.balance?.effective_balance_micros??u.balance?.amount_micros??0,locale:m.value,numberOptions:{minimumFractionDigits:0,maximumFractionDigits:0}}));return($,C)=>k.value?(p(),x("div",pe,[r("div",me,[n(a(F),{shape:"circle",width:"1.5rem",height:"1.5rem"})]),C[0]||(C[0]=r("div",{class:"flex-1"},null,-1)),n(a(F),{width:"8rem",height:"2rem"})])):(p(),x("div",ve,[$.showCreditsOnly?I("",!0):(p(),U(a(W),{key:0,severity:"secondary",rounded:"",class:"p-1 text-amber-400"},{icon:c(()=>C[1]||(C[1]=[r("i",{class:"icon-[lucide--component]"},null,-1)])),_:1})),r("div",{class:G($.textClass)},i($.showCreditsOnly?d.value:v.value),3)]))}}),_e=fe;let N=(function(f){return f.CREDIT_ADDED="credit_added",f.ACCOUNT_CREATED="account_created",f.API_USAGE_STARTED="api_usage_started",f.API_USAGE_COMPLETED="api_usage_completed",f})({});var z=J.create({baseURL:Y(),headers:{"Content-Type":"application/json"}});const ge=o(()=>{const f=E(!1),u=E(null),{d:k}=X();K(()=>Y(),e=>{z.defaults.baseURL=e});const w=o((e,s,b)=>{if(ie(e))return;let h;if(!J.isAxiosError(e))h=`${s} failed: ${e instanceof Error?e.message:String(e)}`;else{const y=e,l=y.response?.status;l&&b?.[l]?h=b[l]:h=y.response?.data?.message??`${s} failed with status ${l}`}u.value=h},"handleRequestError"),m=o(async(e,s)=>{const{errorContext:b,routeSpecificErrors:h}=s;f.value=!0,u.value=null;try{return(await e()).data}catch(y){return w(y,b,h),null}finally{f.value=!1}},"executeRequest");function v(e){switch(e){case"credit_added":return"Credits Added";case"account_created":return"Account Created";case"api_usage_completed":return"API Usage";default:return e}}o(v,"formatEventType");function d(e){return k(new Date(e),{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit"})}o(d,"formatDate");function $(e){return e.split("_").map(s=>s.charAt(0).toUpperCase()+s.slice(1)).join(" ")}o($,"formatJsonKey");function C(e){return typeof e=="number"?e.toLocaleString():typeof e=="string"&&e.match(/^\d{4}-\d{2}-\d{2}/)?new Date(e).toLocaleString():e}o(C,"formatJsonValue");function A(e){switch(e){case"credit_added":return"success";case"account_created":return"info";case"api_usage_completed":return"warning";default:return"info"}}o(A,"getEventSeverity");function O(e){const{amount:s,api_name:b,model:h,...y}=e.params||{};return Object.keys(y).length>0}o(O,"hasAdditionalInfo");function P(e){const{...s}=e.params||{};return Object.entries(s).map(([b,h])=>`<strong>${$(b)}:</strong> ${C(h)}`).join("<br>")}o(P,"getTooltipContent");function t(e){return e?(e/100).toFixed(2):"0.00"}o(t,"formatAmount");async function g({page:e=1,limit:s=10}={}){const b="Fetching customer events",h={400:"Invalid input, object invalid",404:"Not found"},y=await q().getAuthHeader();return y?await m(()=>z.get("/customers/events",{params:{page:e,limit:s},headers:y}),{errorContext:b,routeSpecificErrors:h}):(u.value="Authentication header is missing",null)}return o(g,"getMyEvents"),{isLoading:f,error:u,getMyEvents:g,formatEventType:v,getEventSeverity:A,formatAmount:t,hasAdditionalInfo:O,formatDate:d,formatJsonKey:$,formatJsonValue:C,getTooltipContent:P}},"useCustomerEventsService");var he={key:0,class:"flex items-center justify-center p-8"},ye={key:1,class:"p-4"},Ce={class:"event-details"},be={key:0,class:"font-semibold text-green-500"},$e={key:1},xe={key:2,class:"flex flex-col gap-1"},ke={class:"font-semibold"},we={class:"text-sm text-smoke-400"},Ae=j({__name:"UsageLogsTable",setup(f,{expose:u}){const k=E([]),w=E(!0),m=E(null),v=ge(),d=E({page:1,limit:7,total:0,totalPages:0}),$=D(()=>(d.value.page-1)*d.value.limit),C=D(()=>{const t=new Map;return k.value.forEach(g=>{v.hasAdditionalInfo(g)&&g.event_id&&t.set(g.event_id,v.getTooltipContent(g))}),t}),A=o(async()=>{w.value=!0,m.value=null;try{const t=await v.getMyEvents({page:d.value.page,limit:d.value.limit});t?(t.events&&(k.value=t.events),t.page&&(d.value.page=t.page),t.limit&&(d.value.limit=t.limit),t.total&&(d.value.total=t.total),t.totalPages&&(d.value.totalPages=t.totalPages),R()?.checkForCompletedTopup(t.events)):m.value=v.error.value||"Failed to load events"}catch(t){m.value=t instanceof Error?t.message:"Unknown error",console.error("Error loading events:",t)}finally{w.value=!1}},"loadEvents"),O=o(t=>{d.value.page=t.page+1,A().catch(g=>{console.error("Error loading events:",g)})},"onPageChange");return u({refresh:o(async()=>{d.value.page=1,await A()},"refresh")}),(t,g)=>{const e=re("tooltip");return p(),x("div",null,[w.value?(p(),x("div",he,[n(a(Q))])):m.value?(p(),x("div",ye,[n(a(ae),{severity:"error",closable:!1},{default:c(()=>[T(i(m.value),1)]),_:1})])):(p(),U(a(V),{key:2,value:k.value,paginator:!0,rows:d.value.limit,"total-records":d.value.total,first:$.value,lazy:!0,class:"p-datatable-sm custom-datatable",onPage:O},{default:c(()=>[n(a(B),{field:"event_type",header:t.$t("credits.eventType")},{body:c(({data:s})=>[n(a(te),{value:a(v).formatEventType(s.event_type),severity:a(v).getEventSeverity(s.event_type)},null,8,["value","severity"])]),_:1},8,["header"]),n(a(B),{field:"details",header:t.$t("credits.details")},{body:c(({data:s})=>[r("div",Ce,[s.event_type===a(N).CREDIT_ADDED?(p(),x("div",be,i(t.$t("credits.added"))+" $"+i(a(v).formatAmount(s.params?.amount)),1)):s.event_type===a(N).ACCOUNT_CREATED?(p(),x("div",$e,i(t.$t("credits.accountInitialized")),1)):s.event_type===a(N).API_USAGE_COMPLETED?(p(),x("div",xe,[r("div",ke,i(s.params?.api_name||"API"),1),r("div",we,i(t.$t("credits.model"))+": "+i(s.params?.model||"-"),1)])):I("",!0)])]),_:1},8,["header"]),n(a(B),{field:"createdAt",header:t.$t("credits.time")},{body:c(({data:s})=>[T(i(a(v).formatDate(s.createdAt)),1)]),_:1},8,["header"]),n(a(B),{field:"params",header:t.$t("credits.additionalInfo")},{body:c(({data:s})=>[a(v).hasAdditionalInfo(s)?se((p(),U(S,{key:0,variant:"textonly",size:"icon-sm","aria-label":t.$t("credits.additionalInfo")},{default:c(()=>g[0]||(g[0]=[r("i",{class:"pi pi-info-circle"},null,-1)])),_:2},1032,["aria-label"])),[[e,{escape:!1,value:C.value.get(s.event_id)||"",pt:{text:{style:{width:"max-content !important"}}}},void 0,{top:!0}]]):I("",!0)]),_:1},8,["header"])]),_:1},8,["value","rows","total-records","first"]))])}}}),Ee=Ae,De={class:"flex h-full flex-col"},Se={class:"mb-2 text-2xl font-bold"},Te={class:"flex flex-col gap-2"},Ue={class:"text-sm font-medium text-muted"},Pe={class:"flex items-center justify-between"},Le={class:"flex flex-row items-center"},Be={key:1,class:"text-xs text-muted"},Ie={class:"flex items-center justify-between"},Oe={key:0,class:"grow"},Fe={class:"text-sm font-medium"},Ne={class:"text-xs text-muted"},Re={class:"flex flex-row gap-2"},je=j({__name:"LegacyCreditsPanel",setup(f){const{buildDocsUrl:u,docsPaths:k}=ne(),w=ue(),m=q(),v=le(),d=ce(),$=R(),{isActiveSubscription:C}=de(),A=D(()=>m.loading),O=D(()=>m.isFetchingBalance),P=E(null),t=D(()=>m.lastBalanceUpdateTime?m.lastBalanceUpdateTime.toLocaleString():"");K(()=>m.lastBalanceUpdateTime,(l,_)=>{l&&l!==_&&P.value&&P.value.refresh()});const g=o(()=>{R()?.trackAddApiCreditButtonClicked(),w.showTopUpCreditsDialog()},"handlePurchaseCreditsClick"),e=o(async()=>{await v.accessBillingPortal()},"handleCreditsHistoryClick"),s=o(async()=>{$?.trackHelpResourceClicked({resource_type:"help_feedback",is_external:!0,source:"credits_panel"}),await d.execute("Comfy.ContactSupport")},"handleMessageSupport"),b=o(()=>{window.open(u("/tutorials/api-nodes/faq",{includeLocale:!0}),"_blank")},"handleFaqClick"),h=o(()=>{window.open(u(k.partnerNodesPricing,{includeLocale:!0}),"_blank")},"handleOpenPartnerNodesInfo"),y=E([]);return(l,_)=>(p(),U(a(ee),{value:"Credits",class:"credits-container h-full"},{default:c(()=>[r("div",De,[r("h2",Se,i(l.$t("credits.credits")),1),n(a(H)),r("div",Te,[r("h3",Ue,i(l.$t("credits.yourCreditBalance")),1),r("div",Pe,[n(_e,{"text-class":"text-3xl font-bold"}),A.value?(p(),U(a(F),{key:0,width:"2rem",height:"2rem"})):a(C)?(p(),U(S,{key:1,loading:A.value,onClick:g},{default:c(()=>[T(i(l.$t("credits.purchaseCredits")),1)]),_:1},8,["loading"])):I("",!0)]),r("div",Le,[O.value?(p(),U(a(F),{key:0,width:"12rem",height:"1rem",class:"text-xs"})):t.value?(p(),x("div",Be,i(l.$t("credits.lastUpdated"))+": "+i(t.value),1)):I("",!0),n(S,{variant:"muted-textonly",size:"icon-sm","aria-label":l.$t("g.refresh"),onClick:_[0]||(_[0]=()=>a(v).fetchBalance())},{default:c(()=>_[1]||(_[1]=[r("i",{class:"pi pi-refresh"},null,-1)])),_:1},8,["aria-label"])])]),r("div",Ie,[r("h3",null,i(l.$t("credits.activity")),1),n(S,{variant:"muted-textonly",loading:A.value,onClick:e},{default:c(()=>[_[2]||(_[2]=r("i",{class:"pi pi-arrow-up-right"},null,-1)),T(" "+i(l.$t("credits.invoiceHistory")),1)]),_:1},8,["loading"])]),y.value.length>0?(p(),x("div",Oe,[n(a(V),{value:y.value,"show-headers":!1},{default:c(()=>[n(a(B),{field:"title",header:l.$t("g.name")},{body:c(({data:L})=>[r("div",Fe,i(L.title),1),r("div",Ne,i(L.timestamp),1)]),_:1},8,["header"]),n(a(B),{field:"amount",header:l.$t("g.amount")},{body:c(({data:L})=>[r("div",{class:G(["text-center text-base font-medium",L.isPositive?"text-sky-500":"text-red-400"])},i(L.isPositive?"+":"-")+"$"+i(a(oe)(L.amount,"usd")),3)]),_:1},8,["header"])]),_:1},8,["value"])])):I("",!0),n(a(H)),n(Ee,{ref_key:"usageLogsTableRef",ref:P},null,512),r("div",Re,[n(S,{variant:"muted-textonly",onClick:b},{default:c(()=>[_[3]||(_[3]=r("i",{class:"pi pi-question-circle"},null,-1)),T(" "+i(l.$t("credits.faqs")),1)]),_:1}),n(S,{variant:"muted-textonly",onClick:h},{default:c(()=>[_[4]||(_[4]=r("i",{class:"pi pi-question-circle"},null,-1)),T(" "+i(l.$t("subscription.partnerNodesCredits")),1)]),_:1}),n(S,{variant:"muted-textonly",onClick:s},{default:c(()=>[_[5]||(_[5]=r("i",{class:"pi pi-comments"},null,-1)),T(" "+i(l.$t("credits.messageSupport")),1)]),_:1})])])]),_:1}))}}),st=je;export{st as default};
|
|
2
2
|
|
|
3
|
-
//# sourceMappingURL=LegacyCreditsPanel-
|
|
3
|
+
//# sourceMappingURL=LegacyCreditsPanel-D-CboO8k.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LegacyCreditsPanel-6vR8koQy.js","names":[],"sources":["../../src/components/common/UserCredit.vue","../../src/components/common/UserCredit.vue","../../src/services/customerEventsService.ts","../../src/components/dialog/content/setting/UsageLogsTable.vue","../../src/components/dialog/content/setting/UsageLogsTable.vue","../../src/components/dialog/content/setting/LegacyCreditsPanel.vue","../../src/components/dialog/content/setting/LegacyCreditsPanel.vue"],"sourcesContent":["<template>\n <div v-if=\"balanceLoading\" class=\"flex items-center gap-1\">\n <div class=\"flex items-center gap-2\">\n <Skeleton shape=\"circle\" width=\"1.5rem\" height=\"1.5rem\" />\n </div>\n <div class=\"flex-1\"></div>\n <Skeleton width=\"8rem\" height=\"2rem\" />\n </div>\n <div v-else class=\"flex items-center gap-1\">\n <Tag\n v-if=\"!showCreditsOnly\"\n severity=\"secondary\"\n rounded\n class=\"p-1 text-amber-400\"\n >\n <template #icon>\n <i class=\"icon-[lucide--component]\" />\n </template>\n </Tag>\n <div :class=\"textClass\">\n {{ showCreditsOnly ? formattedCreditsOnly : formattedBalance }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\nconst { textClass, showCreditsOnly } = defineProps<{\n textClass?: string\n showCreditsOnly?: boolean\n}>()\n\nconst authStore = useFirebaseAuthStore()\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\nconst { t, locale } = useI18n()\n\nconst formattedBalance = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value\n })\n return `${amount} ${t('credits.credits')}`\n})\n\nconst formattedCreditsOnly = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n })\n return amount\n})\n</script>\n","<template>\n <div v-if=\"balanceLoading\" class=\"flex items-center gap-1\">\n <div class=\"flex items-center gap-2\">\n <Skeleton shape=\"circle\" width=\"1.5rem\" height=\"1.5rem\" />\n </div>\n <div class=\"flex-1\"></div>\n <Skeleton width=\"8rem\" height=\"2rem\" />\n </div>\n <div v-else class=\"flex items-center gap-1\">\n <Tag\n v-if=\"!showCreditsOnly\"\n severity=\"secondary\"\n rounded\n class=\"p-1 text-amber-400\"\n >\n <template #icon>\n <i class=\"icon-[lucide--component]\" />\n </template>\n </Tag>\n <div :class=\"textClass\">\n {{ showCreditsOnly ? formattedCreditsOnly : formattedBalance }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\nconst { textClass, showCreditsOnly } = defineProps<{\n textClass?: string\n showCreditsOnly?: boolean\n}>()\n\nconst authStore = useFirebaseAuthStore()\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\nconst { t, locale } = useI18n()\n\nconst formattedBalance = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value\n })\n return `${amount} ${t('credits.credits')}`\n})\n\nconst formattedCreditsOnly = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n })\n return amount\n})\n</script>\n","import type { AxiosError, AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { ref, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { getComfyApiBaseUrl } from '@/config/comfyApi'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport type { components, operations } from '@/types/comfyRegistryTypes'\nimport { isAbortError } from '@/utils/typeGuardUtil'\n\nexport enum EventType {\n CREDIT_ADDED = 'credit_added',\n ACCOUNT_CREATED = 'account_created',\n API_USAGE_STARTED = 'api_usage_started',\n API_USAGE_COMPLETED = 'api_usage_completed'\n}\n\ntype CustomerEventsResponse =\n operations['GetCustomerEvents']['responses']['200']['content']['application/json']\n\ntype CustomerEventsResponseQuery =\n operations['GetCustomerEvents']['parameters']['query']\n\nexport type AuditLog = components['schemas']['AuditLog']\n\nconst customerApiClient = axios.create({\n baseURL: getComfyApiBaseUrl(),\n headers: {\n 'Content-Type': 'application/json'\n }\n})\n\nexport const useCustomerEventsService = () => {\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n const { d } = useI18n()\n\n watch(\n () => getComfyApiBaseUrl(),\n (url) => {\n customerApiClient.defaults.baseURL = url\n }\n )\n\n const handleRequestError = (\n err: unknown,\n context: string,\n routeSpecificErrors?: Record<number, string>\n ) => {\n // Don't treat cancellation as an error\n if (isAbortError(err)) return\n\n let message: string\n if (!axios.isAxiosError(err)) {\n message = `${context} failed: ${err instanceof Error ? err.message : String(err)}`\n } else {\n const axiosError = err as AxiosError<{ message: string }>\n const status = axiosError.response?.status\n if (status && routeSpecificErrors?.[status]) {\n message = routeSpecificErrors[status]\n } else {\n message =\n axiosError.response?.data?.message ??\n `${context} failed with status ${status}`\n }\n }\n\n error.value = message\n }\n\n const executeRequest = async <T>(\n requestCall: () => Promise<AxiosResponse<T>>,\n options: {\n errorContext: string\n routeSpecificErrors?: Record<number, string>\n }\n ): Promise<T | null> => {\n const { errorContext, routeSpecificErrors } = options\n\n isLoading.value = true\n error.value = null\n\n try {\n const response = await requestCall()\n return response.data\n } catch (err) {\n handleRequestError(err, errorContext, routeSpecificErrors)\n return null\n } finally {\n isLoading.value = false\n }\n }\n\n function formatEventType(eventType: string) {\n switch (eventType) {\n case 'credit_added':\n return 'Credits Added'\n case 'account_created':\n return 'Account Created'\n case 'api_usage_completed':\n return 'API Usage'\n default:\n return eventType\n }\n }\n\n function formatDate(dateString: string): string {\n const date = new Date(dateString)\n\n return d(date, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n })\n }\n\n function formatJsonKey(key: string) {\n return key\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n }\n\n function formatJsonValue(value: any) {\n if (typeof value === 'number') {\n // Format numbers with commas and decimals if needed\n return value.toLocaleString()\n }\n if (typeof value === 'string' && value.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n // Format dates nicely\n return new Date(value).toLocaleString()\n }\n return value\n }\n\n function getEventSeverity(eventType: string) {\n switch (eventType) {\n case 'credit_added':\n return 'success'\n case 'account_created':\n return 'info'\n case 'api_usage_completed':\n return 'warning'\n default:\n return 'info'\n }\n }\n\n function hasAdditionalInfo(event: AuditLog) {\n const { amount, api_name, model, ...otherParams } = event.params || {}\n return Object.keys(otherParams).length > 0\n }\n\n function getTooltipContent(event: AuditLog) {\n const { ...params } = event.params || {}\n\n return Object.entries(params)\n .map(([key, value]) => {\n const formattedKey = formatJsonKey(key)\n const formattedValue = formatJsonValue(value)\n return `<strong>${formattedKey}:</strong> ${formattedValue}`\n })\n .join('<br>')\n }\n\n function formatAmount(amountMicros?: number) {\n if (!amountMicros) return '0.00'\n return (amountMicros / 100).toFixed(2)\n }\n\n async function getMyEvents({\n page = 1,\n limit = 10\n }: CustomerEventsResponseQuery = {}): Promise<CustomerEventsResponse | null> {\n const errorContext = 'Fetching customer events'\n const routeSpecificErrors = {\n 400: 'Invalid input, object invalid',\n 404: 'Not found'\n }\n\n // Get auth headers\n const authHeaders = await useFirebaseAuthStore().getAuthHeader()\n if (!authHeaders) {\n error.value = 'Authentication header is missing'\n return null\n }\n\n const result = await executeRequest<CustomerEventsResponse>(\n () =>\n customerApiClient.get('/customers/events', {\n params: { page, limit },\n headers: authHeaders\n }),\n { errorContext, routeSpecificErrors }\n )\n\n return result\n }\n\n return {\n // State\n isLoading,\n error,\n\n // Methods\n getMyEvents,\n formatEventType,\n getEventSeverity,\n formatAmount,\n hasAdditionalInfo,\n formatDate,\n formatJsonKey,\n formatJsonValue,\n getTooltipContent\n }\n}\n","<template>\n <div>\n <div v-if=\"loading\" class=\"flex items-center justify-center p-8\">\n <ProgressSpinner />\n </div>\n <div v-else-if=\"error\" class=\"p-4\">\n <Message severity=\"error\" :closable=\"false\">{{ error }}</Message>\n </div>\n <DataTable\n v-else\n :value=\"events\"\n :paginator=\"true\"\n :rows=\"pagination.limit\"\n :total-records=\"pagination.total\"\n :first=\"dataTableFirst\"\n :lazy=\"true\"\n class=\"p-datatable-sm custom-datatable\"\n @page=\"onPageChange\"\n >\n <Column field=\"event_type\" :header=\"$t('credits.eventType')\">\n <template #body=\"{ data }\">\n <Badge\n :value=\"customerEventService.formatEventType(data.event_type)\"\n :severity=\"customerEventService.getEventSeverity(data.event_type)\"\n />\n </template>\n </Column>\n <Column field=\"details\" :header=\"$t('credits.details')\">\n <template #body=\"{ data }\">\n <div class=\"event-details\">\n <!-- Credits Added -->\n <template v-if=\"data.event_type === EventType.CREDIT_ADDED\">\n <div class=\"font-semibold text-green-500\">\n {{ $t('credits.added') }} ${{\n customerEventService.formatAmount(data.params?.amount)\n }}\n </div>\n </template>\n\n <!-- Account Created -->\n <template v-else-if=\"data.event_type === EventType.ACCOUNT_CREATED\">\n <div>{{ $t('credits.accountInitialized') }}</div>\n </template>\n\n <!-- API Usage -->\n <template\n v-else-if=\"data.event_type === EventType.API_USAGE_COMPLETED\"\n >\n <div class=\"flex flex-col gap-1\">\n <div class=\"font-semibold\">\n {{ data.params?.api_name || 'API' }}\n </div>\n <div class=\"text-sm text-smoke-400\">\n {{ $t('credits.model') }}: {{ data.params?.model || '-' }}\n </div>\n </div>\n </template>\n </div>\n </template>\n </Column>\n <Column field=\"createdAt\" :header=\"$t('credits.time')\">\n <template #body=\"{ data }\">\n {{ customerEventService.formatDate(data.createdAt) }}\n </template>\n </Column>\n <Column field=\"params\" :header=\"$t('credits.additionalInfo')\">\n <template #body=\"{ data }\">\n <Button\n v-if=\"customerEventService.hasAdditionalInfo(data)\"\n v-tooltip.top=\"{\n escape: false,\n value: tooltipContentMap.get(data.event_id) || '',\n pt: {\n text: {\n style: {\n width: 'max-content !important'\n }\n }\n }\n }\"\n variant=\"textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('credits.additionalInfo')\"\n >\n <i class=\"pi pi-info-circle\" />\n </Button>\n </template>\n </Column>\n </DataTable>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Badge from 'primevue/badge'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Message from 'primevue/message'\nimport ProgressSpinner from 'primevue/progressspinner'\nimport { computed, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useTelemetry } from '@/platform/telemetry'\nimport type { AuditLog } from '@/services/customerEventsService'\nimport {\n EventType,\n useCustomerEventsService\n} from '@/services/customerEventsService'\n\nconst events = ref<AuditLog[]>([])\nconst loading = ref(true)\nconst error = ref<string | null>(null)\n\nconst customerEventService = useCustomerEventsService()\n\nconst pagination = ref({\n page: 1,\n limit: 7,\n total: 0,\n totalPages: 0\n})\n\nconst dataTableFirst = computed(\n () => (pagination.value.page - 1) * pagination.value.limit\n)\n\nconst tooltipContentMap = computed(() => {\n const map = new Map<string, string>()\n events.value.forEach((event) => {\n if (customerEventService.hasAdditionalInfo(event) && event.event_id) {\n map.set(event.event_id, customerEventService.getTooltipContent(event))\n }\n })\n return map\n})\n\nconst loadEvents = async () => {\n loading.value = true\n error.value = null\n\n try {\n const response = await customerEventService.getMyEvents({\n page: pagination.value.page,\n limit: pagination.value.limit\n })\n\n if (response) {\n if (response.events) {\n events.value = response.events\n }\n\n if (response.page) {\n pagination.value.page = response.page\n }\n\n if (response.limit) {\n pagination.value.limit = response.limit\n }\n\n if (response.total) {\n pagination.value.total = response.total\n }\n\n if (response.totalPages) {\n pagination.value.totalPages = response.totalPages\n }\n\n // Check if a pending top-up has completed\n useTelemetry()?.checkForCompletedTopup(response.events)\n } else {\n error.value = customerEventService.error.value || 'Failed to load events'\n }\n } catch (err) {\n error.value = err instanceof Error ? err.message : 'Unknown error'\n console.error('Error loading events:', err)\n } finally {\n loading.value = false\n }\n}\n\nconst onPageChange = (event: { page: number }) => {\n pagination.value.page = event.page + 1\n loadEvents().catch((error) => {\n console.error('Error loading events:', error)\n })\n}\n\nconst refresh = async () => {\n pagination.value.page = 1\n await loadEvents()\n}\n\ndefineExpose({\n refresh\n})\n</script>\n","<template>\n <div>\n <div v-if=\"loading\" class=\"flex items-center justify-center p-8\">\n <ProgressSpinner />\n </div>\n <div v-else-if=\"error\" class=\"p-4\">\n <Message severity=\"error\" :closable=\"false\">{{ error }}</Message>\n </div>\n <DataTable\n v-else\n :value=\"events\"\n :paginator=\"true\"\n :rows=\"pagination.limit\"\n :total-records=\"pagination.total\"\n :first=\"dataTableFirst\"\n :lazy=\"true\"\n class=\"p-datatable-sm custom-datatable\"\n @page=\"onPageChange\"\n >\n <Column field=\"event_type\" :header=\"$t('credits.eventType')\">\n <template #body=\"{ data }\">\n <Badge\n :value=\"customerEventService.formatEventType(data.event_type)\"\n :severity=\"customerEventService.getEventSeverity(data.event_type)\"\n />\n </template>\n </Column>\n <Column field=\"details\" :header=\"$t('credits.details')\">\n <template #body=\"{ data }\">\n <div class=\"event-details\">\n <!-- Credits Added -->\n <template v-if=\"data.event_type === EventType.CREDIT_ADDED\">\n <div class=\"font-semibold text-green-500\">\n {{ $t('credits.added') }} ${{\n customerEventService.formatAmount(data.params?.amount)\n }}\n </div>\n </template>\n\n <!-- Account Created -->\n <template v-else-if=\"data.event_type === EventType.ACCOUNT_CREATED\">\n <div>{{ $t('credits.accountInitialized') }}</div>\n </template>\n\n <!-- API Usage -->\n <template\n v-else-if=\"data.event_type === EventType.API_USAGE_COMPLETED\"\n >\n <div class=\"flex flex-col gap-1\">\n <div class=\"font-semibold\">\n {{ data.params?.api_name || 'API' }}\n </div>\n <div class=\"text-sm text-smoke-400\">\n {{ $t('credits.model') }}: {{ data.params?.model || '-' }}\n </div>\n </div>\n </template>\n </div>\n </template>\n </Column>\n <Column field=\"createdAt\" :header=\"$t('credits.time')\">\n <template #body=\"{ data }\">\n {{ customerEventService.formatDate(data.createdAt) }}\n </template>\n </Column>\n <Column field=\"params\" :header=\"$t('credits.additionalInfo')\">\n <template #body=\"{ data }\">\n <Button\n v-if=\"customerEventService.hasAdditionalInfo(data)\"\n v-tooltip.top=\"{\n escape: false,\n value: tooltipContentMap.get(data.event_id) || '',\n pt: {\n text: {\n style: {\n width: 'max-content !important'\n }\n }\n }\n }\"\n variant=\"textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('credits.additionalInfo')\"\n >\n <i class=\"pi pi-info-circle\" />\n </Button>\n </template>\n </Column>\n </DataTable>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Badge from 'primevue/badge'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Message from 'primevue/message'\nimport ProgressSpinner from 'primevue/progressspinner'\nimport { computed, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useTelemetry } from '@/platform/telemetry'\nimport type { AuditLog } from '@/services/customerEventsService'\nimport {\n EventType,\n useCustomerEventsService\n} from '@/services/customerEventsService'\n\nconst events = ref<AuditLog[]>([])\nconst loading = ref(true)\nconst error = ref<string | null>(null)\n\nconst customerEventService = useCustomerEventsService()\n\nconst pagination = ref({\n page: 1,\n limit: 7,\n total: 0,\n totalPages: 0\n})\n\nconst dataTableFirst = computed(\n () => (pagination.value.page - 1) * pagination.value.limit\n)\n\nconst tooltipContentMap = computed(() => {\n const map = new Map<string, string>()\n events.value.forEach((event) => {\n if (customerEventService.hasAdditionalInfo(event) && event.event_id) {\n map.set(event.event_id, customerEventService.getTooltipContent(event))\n }\n })\n return map\n})\n\nconst loadEvents = async () => {\n loading.value = true\n error.value = null\n\n try {\n const response = await customerEventService.getMyEvents({\n page: pagination.value.page,\n limit: pagination.value.limit\n })\n\n if (response) {\n if (response.events) {\n events.value = response.events\n }\n\n if (response.page) {\n pagination.value.page = response.page\n }\n\n if (response.limit) {\n pagination.value.limit = response.limit\n }\n\n if (response.total) {\n pagination.value.total = response.total\n }\n\n if (response.totalPages) {\n pagination.value.totalPages = response.totalPages\n }\n\n // Check if a pending top-up has completed\n useTelemetry()?.checkForCompletedTopup(response.events)\n } else {\n error.value = customerEventService.error.value || 'Failed to load events'\n }\n } catch (err) {\n error.value = err instanceof Error ? err.message : 'Unknown error'\n console.error('Error loading events:', err)\n } finally {\n loading.value = false\n }\n}\n\nconst onPageChange = (event: { page: number }) => {\n pagination.value.page = event.page + 1\n loadEvents().catch((error) => {\n console.error('Error loading events:', error)\n })\n}\n\nconst refresh = async () => {\n pagination.value.page = 1\n await loadEvents()\n}\n\ndefineExpose({\n refresh\n})\n</script>\n","<template>\n <TabPanel value=\"Credits\" class=\"credits-container h-full\">\n <!-- Legacy Design -->\n <div class=\"flex h-full flex-col\">\n <h2 class=\"mb-2 text-2xl font-bold\">\n {{ $t('credits.credits') }}\n </h2>\n\n <Divider />\n\n <div class=\"flex flex-col gap-2\">\n <h3 class=\"text-sm font-medium text-muted\">\n {{ $t('credits.yourCreditBalance') }}\n </h3>\n <div class=\"flex items-center justify-between\">\n <UserCredit text-class=\"text-3xl font-bold\" />\n <Skeleton v-if=\"loading\" width=\"2rem\" height=\"2rem\" />\n <Button\n v-else-if=\"isActiveSubscription\"\n :loading=\"loading\"\n @click=\"handlePurchaseCreditsClick\"\n >\n {{ $t('credits.purchaseCredits') }}\n </Button>\n </div>\n <div class=\"flex flex-row items-center\">\n <Skeleton\n v-if=\"balanceLoading\"\n width=\"12rem\"\n height=\"1rem\"\n class=\"text-xs\"\n />\n <div v-else-if=\"formattedLastUpdateTime\" class=\"text-xs text-muted\">\n {{ $t('credits.lastUpdated') }}: {{ formattedLastUpdateTime }}\n </div>\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('g.refresh')\"\n @click=\"() => authActions.fetchBalance()\"\n >\n <i class=\"pi pi-refresh\" />\n </Button>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between\">\n <h3>{{ $t('credits.activity') }}</h3>\n <Button\n variant=\"muted-textonly\"\n :loading=\"loading\"\n @click=\"handleCreditsHistoryClick\"\n >\n <i class=\"pi pi-arrow-up-right\" />\n {{ $t('credits.invoiceHistory') }}\n </Button>\n </div>\n\n <template v-if=\"creditHistory.length > 0\">\n <div class=\"grow\">\n <DataTable :value=\"creditHistory\" :show-headers=\"false\">\n <Column field=\"title\" :header=\"$t('g.name')\">\n <template #body=\"{ data }\">\n <div class=\"text-sm font-medium\">{{ data.title }}</div>\n <div class=\"text-xs text-muted\">{{ data.timestamp }}</div>\n </template>\n </Column>\n <Column field=\"amount\" :header=\"$t('g.amount')\">\n <template #body=\"{ data }\">\n <div\n :class=\"[\n 'text-center text-base font-medium',\n data.isPositive ? 'text-sky-500' : 'text-red-400'\n ]\"\n >\n {{ data.isPositive ? '+' : '-' }}${{\n formatMetronomeCurrency(data.amount, 'usd')\n }}\n </div>\n </template>\n </Column>\n </DataTable>\n </div>\n </template>\n\n <Divider />\n\n <UsageLogsTable ref=\"usageLogsTableRef\" />\n\n <div class=\"flex flex-row gap-2\">\n <Button variant=\"muted-textonly\" @click=\"handleFaqClick\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('credits.faqs') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleOpenPartnerNodesInfo\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleMessageSupport\">\n <i class=\"pi pi-comments\" />\n {{ $t('credits.messageSupport') }}\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Divider from 'primevue/divider'\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, ref, watch } from 'vue'\n\nimport UserCredit from '@/components/common/UserCredit.vue'\nimport UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport { formatMetronomeCurrency } from '@/utils/formatUtil'\n\ninterface CreditHistoryItemData {\n title: string\n timestamp: string\n amount: number\n isPositive: boolean\n}\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst dialogService = useDialogService()\nconst authStore = useFirebaseAuthStore()\nconst authActions = useFirebaseAuthActions()\nconst commandStore = useCommandStore()\nconst telemetry = useTelemetry()\nconst { isActiveSubscription } = useSubscription()\nconst loading = computed(() => authStore.loading)\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\n\nconst usageLogsTableRef = ref<InstanceType<typeof UsageLogsTable> | null>(null)\n\nconst formattedLastUpdateTime = computed(() =>\n authStore.lastBalanceUpdateTime\n ? authStore.lastBalanceUpdateTime.toLocaleString()\n : ''\n)\n\nwatch(\n () => authStore.lastBalanceUpdateTime,\n (newTime, oldTime) => {\n if (newTime && newTime !== oldTime && usageLogsTableRef.value) {\n usageLogsTableRef.value.refresh()\n }\n }\n)\n\nconst handlePurchaseCreditsClick = () => {\n // Track purchase credits entry from Settings > Credits panel\n useTelemetry()?.trackAddApiCreditButtonClicked()\n dialogService.showTopUpCreditsDialog()\n}\n\nconst handleCreditsHistoryClick = async () => {\n await authActions.accessBillingPortal()\n}\n\nconst handleMessageSupport = async () => {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'credits_panel'\n })\n await commandStore.execute('Comfy.ContactSupport')\n}\n\nconst handleFaqClick = () => {\n window.open(\n buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }),\n '_blank'\n )\n}\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n\nconst creditHistory = ref<CreditHistoryItemData[]>([])\n</script>\n","<template>\n <TabPanel value=\"Credits\" class=\"credits-container h-full\">\n <!-- Legacy Design -->\n <div class=\"flex h-full flex-col\">\n <h2 class=\"mb-2 text-2xl font-bold\">\n {{ $t('credits.credits') }}\n </h2>\n\n <Divider />\n\n <div class=\"flex flex-col gap-2\">\n <h3 class=\"text-sm font-medium text-muted\">\n {{ $t('credits.yourCreditBalance') }}\n </h3>\n <div class=\"flex items-center justify-between\">\n <UserCredit text-class=\"text-3xl font-bold\" />\n <Skeleton v-if=\"loading\" width=\"2rem\" height=\"2rem\" />\n <Button\n v-else-if=\"isActiveSubscription\"\n :loading=\"loading\"\n @click=\"handlePurchaseCreditsClick\"\n >\n {{ $t('credits.purchaseCredits') }}\n </Button>\n </div>\n <div class=\"flex flex-row items-center\">\n <Skeleton\n v-if=\"balanceLoading\"\n width=\"12rem\"\n height=\"1rem\"\n class=\"text-xs\"\n />\n <div v-else-if=\"formattedLastUpdateTime\" class=\"text-xs text-muted\">\n {{ $t('credits.lastUpdated') }}: {{ formattedLastUpdateTime }}\n </div>\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('g.refresh')\"\n @click=\"() => authActions.fetchBalance()\"\n >\n <i class=\"pi pi-refresh\" />\n </Button>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between\">\n <h3>{{ $t('credits.activity') }}</h3>\n <Button\n variant=\"muted-textonly\"\n :loading=\"loading\"\n @click=\"handleCreditsHistoryClick\"\n >\n <i class=\"pi pi-arrow-up-right\" />\n {{ $t('credits.invoiceHistory') }}\n </Button>\n </div>\n\n <template v-if=\"creditHistory.length > 0\">\n <div class=\"grow\">\n <DataTable :value=\"creditHistory\" :show-headers=\"false\">\n <Column field=\"title\" :header=\"$t('g.name')\">\n <template #body=\"{ data }\">\n <div class=\"text-sm font-medium\">{{ data.title }}</div>\n <div class=\"text-xs text-muted\">{{ data.timestamp }}</div>\n </template>\n </Column>\n <Column field=\"amount\" :header=\"$t('g.amount')\">\n <template #body=\"{ data }\">\n <div\n :class=\"[\n 'text-center text-base font-medium',\n data.isPositive ? 'text-sky-500' : 'text-red-400'\n ]\"\n >\n {{ data.isPositive ? '+' : '-' }}${{\n formatMetronomeCurrency(data.amount, 'usd')\n }}\n </div>\n </template>\n </Column>\n </DataTable>\n </div>\n </template>\n\n <Divider />\n\n <UsageLogsTable ref=\"usageLogsTableRef\" />\n\n <div class=\"flex flex-row gap-2\">\n <Button variant=\"muted-textonly\" @click=\"handleFaqClick\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('credits.faqs') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleOpenPartnerNodesInfo\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleMessageSupport\">\n <i class=\"pi pi-comments\" />\n {{ $t('credits.messageSupport') }}\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Divider from 'primevue/divider'\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, ref, watch } from 'vue'\n\nimport UserCredit from '@/components/common/UserCredit.vue'\nimport UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport { formatMetronomeCurrency } from '@/utils/formatUtil'\n\ninterface CreditHistoryItemData {\n title: string\n timestamp: string\n amount: number\n isPositive: boolean\n}\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst dialogService = useDialogService()\nconst authStore = useFirebaseAuthStore()\nconst authActions = useFirebaseAuthActions()\nconst commandStore = useCommandStore()\nconst telemetry = useTelemetry()\nconst { isActiveSubscription } = useSubscription()\nconst loading = computed(() => authStore.loading)\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\n\nconst usageLogsTableRef = ref<InstanceType<typeof UsageLogsTable> | null>(null)\n\nconst formattedLastUpdateTime = computed(() =>\n authStore.lastBalanceUpdateTime\n ? authStore.lastBalanceUpdateTime.toLocaleString()\n : ''\n)\n\nwatch(\n () => authStore.lastBalanceUpdateTime,\n (newTime, oldTime) => {\n if (newTime && newTime !== oldTime && usageLogsTableRef.value) {\n usageLogsTableRef.value.refresh()\n }\n }\n)\n\nconst handlePurchaseCreditsClick = () => {\n // Track purchase credits entry from Settings > Credits panel\n useTelemetry()?.trackAddApiCreditButtonClicked()\n dialogService.showTopUpCreditsDialog()\n}\n\nconst handleCreditsHistoryClick = async () => {\n await authActions.accessBillingPortal()\n}\n\nconst handleMessageSupport = async () => {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'credits_panel'\n })\n await commandStore.execute('Comfy.ContactSupport')\n}\n\nconst handleFaqClick = () => {\n window.open(\n buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }),\n '_blank'\n )\n}\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n\nconst creditHistory = ref<CreditHistoryItemData[]>([])\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LegacyCreditsPanel-D-CboO8k.js","names":[],"sources":["../../src/components/common/UserCredit.vue","../../src/components/common/UserCredit.vue","../../src/services/customerEventsService.ts","../../src/components/dialog/content/setting/UsageLogsTable.vue","../../src/components/dialog/content/setting/UsageLogsTable.vue","../../src/components/dialog/content/setting/LegacyCreditsPanel.vue","../../src/components/dialog/content/setting/LegacyCreditsPanel.vue"],"sourcesContent":["<template>\n <div v-if=\"balanceLoading\" class=\"flex items-center gap-1\">\n <div class=\"flex items-center gap-2\">\n <Skeleton shape=\"circle\" width=\"1.5rem\" height=\"1.5rem\" />\n </div>\n <div class=\"flex-1\"></div>\n <Skeleton width=\"8rem\" height=\"2rem\" />\n </div>\n <div v-else class=\"flex items-center gap-1\">\n <Tag\n v-if=\"!showCreditsOnly\"\n severity=\"secondary\"\n rounded\n class=\"p-1 text-amber-400\"\n >\n <template #icon>\n <i class=\"icon-[lucide--component]\" />\n </template>\n </Tag>\n <div :class=\"textClass\">\n {{ showCreditsOnly ? formattedCreditsOnly : formattedBalance }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\nconst { textClass, showCreditsOnly } = defineProps<{\n textClass?: string\n showCreditsOnly?: boolean\n}>()\n\nconst authStore = useFirebaseAuthStore()\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\nconst { t, locale } = useI18n()\n\nconst formattedBalance = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value\n })\n return `${amount} ${t('credits.credits')}`\n})\n\nconst formattedCreditsOnly = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n })\n return amount\n})\n</script>\n","<template>\n <div v-if=\"balanceLoading\" class=\"flex items-center gap-1\">\n <div class=\"flex items-center gap-2\">\n <Skeleton shape=\"circle\" width=\"1.5rem\" height=\"1.5rem\" />\n </div>\n <div class=\"flex-1\"></div>\n <Skeleton width=\"8rem\" height=\"2rem\" />\n </div>\n <div v-else class=\"flex items-center gap-1\">\n <Tag\n v-if=\"!showCreditsOnly\"\n severity=\"secondary\"\n rounded\n class=\"p-1 text-amber-400\"\n >\n <template #icon>\n <i class=\"icon-[lucide--component]\" />\n </template>\n </Tag>\n <div :class=\"textClass\">\n {{ showCreditsOnly ? formattedCreditsOnly : formattedBalance }}\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Skeleton from 'primevue/skeleton'\nimport Tag from 'primevue/tag'\nimport { computed } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { formatCreditsFromCents } from '@/base/credits/comfyCredits'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\n\nconst { textClass, showCreditsOnly } = defineProps<{\n textClass?: string\n showCreditsOnly?: boolean\n}>()\n\nconst authStore = useFirebaseAuthStore()\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\nconst { t, locale } = useI18n()\n\nconst formattedBalance = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value\n })\n return `${amount} ${t('credits.credits')}`\n})\n\nconst formattedCreditsOnly = computed(() => {\n const cents =\n authStore.balance?.effective_balance_micros ??\n authStore.balance?.amount_micros ??\n 0\n const amount = formatCreditsFromCents({\n cents,\n locale: locale.value,\n numberOptions: { minimumFractionDigits: 0, maximumFractionDigits: 0 }\n })\n return amount\n})\n</script>\n","import type { AxiosError, AxiosResponse } from 'axios'\nimport axios from 'axios'\nimport { ref, watch } from 'vue'\nimport { useI18n } from 'vue-i18n'\n\nimport { getComfyApiBaseUrl } from '@/config/comfyApi'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport type { components, operations } from '@/types/comfyRegistryTypes'\nimport { isAbortError } from '@/utils/typeGuardUtil'\n\nexport enum EventType {\n CREDIT_ADDED = 'credit_added',\n ACCOUNT_CREATED = 'account_created',\n API_USAGE_STARTED = 'api_usage_started',\n API_USAGE_COMPLETED = 'api_usage_completed'\n}\n\ntype CustomerEventsResponse =\n operations['GetCustomerEvents']['responses']['200']['content']['application/json']\n\ntype CustomerEventsResponseQuery =\n operations['GetCustomerEvents']['parameters']['query']\n\nexport type AuditLog = components['schemas']['AuditLog']\n\nconst customerApiClient = axios.create({\n baseURL: getComfyApiBaseUrl(),\n headers: {\n 'Content-Type': 'application/json'\n }\n})\n\nexport const useCustomerEventsService = () => {\n const isLoading = ref(false)\n const error = ref<string | null>(null)\n const { d } = useI18n()\n\n watch(\n () => getComfyApiBaseUrl(),\n (url) => {\n customerApiClient.defaults.baseURL = url\n }\n )\n\n const handleRequestError = (\n err: unknown,\n context: string,\n routeSpecificErrors?: Record<number, string>\n ) => {\n // Don't treat cancellation as an error\n if (isAbortError(err)) return\n\n let message: string\n if (!axios.isAxiosError(err)) {\n message = `${context} failed: ${err instanceof Error ? err.message : String(err)}`\n } else {\n const axiosError = err as AxiosError<{ message: string }>\n const status = axiosError.response?.status\n if (status && routeSpecificErrors?.[status]) {\n message = routeSpecificErrors[status]\n } else {\n message =\n axiosError.response?.data?.message ??\n `${context} failed with status ${status}`\n }\n }\n\n error.value = message\n }\n\n const executeRequest = async <T>(\n requestCall: () => Promise<AxiosResponse<T>>,\n options: {\n errorContext: string\n routeSpecificErrors?: Record<number, string>\n }\n ): Promise<T | null> => {\n const { errorContext, routeSpecificErrors } = options\n\n isLoading.value = true\n error.value = null\n\n try {\n const response = await requestCall()\n return response.data\n } catch (err) {\n handleRequestError(err, errorContext, routeSpecificErrors)\n return null\n } finally {\n isLoading.value = false\n }\n }\n\n function formatEventType(eventType: string) {\n switch (eventType) {\n case 'credit_added':\n return 'Credits Added'\n case 'account_created':\n return 'Account Created'\n case 'api_usage_completed':\n return 'API Usage'\n default:\n return eventType\n }\n }\n\n function formatDate(dateString: string): string {\n const date = new Date(dateString)\n\n return d(date, {\n month: 'short',\n day: 'numeric',\n hour: '2-digit',\n minute: '2-digit'\n })\n }\n\n function formatJsonKey(key: string) {\n return key\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ')\n }\n\n function formatJsonValue(value: any) {\n if (typeof value === 'number') {\n // Format numbers with commas and decimals if needed\n return value.toLocaleString()\n }\n if (typeof value === 'string' && value.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n // Format dates nicely\n return new Date(value).toLocaleString()\n }\n return value\n }\n\n function getEventSeverity(eventType: string) {\n switch (eventType) {\n case 'credit_added':\n return 'success'\n case 'account_created':\n return 'info'\n case 'api_usage_completed':\n return 'warning'\n default:\n return 'info'\n }\n }\n\n function hasAdditionalInfo(event: AuditLog) {\n const { amount, api_name, model, ...otherParams } = event.params || {}\n return Object.keys(otherParams).length > 0\n }\n\n function getTooltipContent(event: AuditLog) {\n const { ...params } = event.params || {}\n\n return Object.entries(params)\n .map(([key, value]) => {\n const formattedKey = formatJsonKey(key)\n const formattedValue = formatJsonValue(value)\n return `<strong>${formattedKey}:</strong> ${formattedValue}`\n })\n .join('<br>')\n }\n\n function formatAmount(amountMicros?: number) {\n if (!amountMicros) return '0.00'\n return (amountMicros / 100).toFixed(2)\n }\n\n async function getMyEvents({\n page = 1,\n limit = 10\n }: CustomerEventsResponseQuery = {}): Promise<CustomerEventsResponse | null> {\n const errorContext = 'Fetching customer events'\n const routeSpecificErrors = {\n 400: 'Invalid input, object invalid',\n 404: 'Not found'\n }\n\n // Get auth headers\n const authHeaders = await useFirebaseAuthStore().getAuthHeader()\n if (!authHeaders) {\n error.value = 'Authentication header is missing'\n return null\n }\n\n const result = await executeRequest<CustomerEventsResponse>(\n () =>\n customerApiClient.get('/customers/events', {\n params: { page, limit },\n headers: authHeaders\n }),\n { errorContext, routeSpecificErrors }\n )\n\n return result\n }\n\n return {\n // State\n isLoading,\n error,\n\n // Methods\n getMyEvents,\n formatEventType,\n getEventSeverity,\n formatAmount,\n hasAdditionalInfo,\n formatDate,\n formatJsonKey,\n formatJsonValue,\n getTooltipContent\n }\n}\n","<template>\n <div>\n <div v-if=\"loading\" class=\"flex items-center justify-center p-8\">\n <ProgressSpinner />\n </div>\n <div v-else-if=\"error\" class=\"p-4\">\n <Message severity=\"error\" :closable=\"false\">{{ error }}</Message>\n </div>\n <DataTable\n v-else\n :value=\"events\"\n :paginator=\"true\"\n :rows=\"pagination.limit\"\n :total-records=\"pagination.total\"\n :first=\"dataTableFirst\"\n :lazy=\"true\"\n class=\"p-datatable-sm custom-datatable\"\n @page=\"onPageChange\"\n >\n <Column field=\"event_type\" :header=\"$t('credits.eventType')\">\n <template #body=\"{ data }\">\n <Badge\n :value=\"customerEventService.formatEventType(data.event_type)\"\n :severity=\"customerEventService.getEventSeverity(data.event_type)\"\n />\n </template>\n </Column>\n <Column field=\"details\" :header=\"$t('credits.details')\">\n <template #body=\"{ data }\">\n <div class=\"event-details\">\n <!-- Credits Added -->\n <template v-if=\"data.event_type === EventType.CREDIT_ADDED\">\n <div class=\"font-semibold text-green-500\">\n {{ $t('credits.added') }} ${{\n customerEventService.formatAmount(data.params?.amount)\n }}\n </div>\n </template>\n\n <!-- Account Created -->\n <template v-else-if=\"data.event_type === EventType.ACCOUNT_CREATED\">\n <div>{{ $t('credits.accountInitialized') }}</div>\n </template>\n\n <!-- API Usage -->\n <template\n v-else-if=\"data.event_type === EventType.API_USAGE_COMPLETED\"\n >\n <div class=\"flex flex-col gap-1\">\n <div class=\"font-semibold\">\n {{ data.params?.api_name || 'API' }}\n </div>\n <div class=\"text-sm text-smoke-400\">\n {{ $t('credits.model') }}: {{ data.params?.model || '-' }}\n </div>\n </div>\n </template>\n </div>\n </template>\n </Column>\n <Column field=\"createdAt\" :header=\"$t('credits.time')\">\n <template #body=\"{ data }\">\n {{ customerEventService.formatDate(data.createdAt) }}\n </template>\n </Column>\n <Column field=\"params\" :header=\"$t('credits.additionalInfo')\">\n <template #body=\"{ data }\">\n <Button\n v-if=\"customerEventService.hasAdditionalInfo(data)\"\n v-tooltip.top=\"{\n escape: false,\n value: tooltipContentMap.get(data.event_id) || '',\n pt: {\n text: {\n style: {\n width: 'max-content !important'\n }\n }\n }\n }\"\n variant=\"textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('credits.additionalInfo')\"\n >\n <i class=\"pi pi-info-circle\" />\n </Button>\n </template>\n </Column>\n </DataTable>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Badge from 'primevue/badge'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Message from 'primevue/message'\nimport ProgressSpinner from 'primevue/progressspinner'\nimport { computed, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useTelemetry } from '@/platform/telemetry'\nimport type { AuditLog } from '@/services/customerEventsService'\nimport {\n EventType,\n useCustomerEventsService\n} from '@/services/customerEventsService'\n\nconst events = ref<AuditLog[]>([])\nconst loading = ref(true)\nconst error = ref<string | null>(null)\n\nconst customerEventService = useCustomerEventsService()\n\nconst pagination = ref({\n page: 1,\n limit: 7,\n total: 0,\n totalPages: 0\n})\n\nconst dataTableFirst = computed(\n () => (pagination.value.page - 1) * pagination.value.limit\n)\n\nconst tooltipContentMap = computed(() => {\n const map = new Map<string, string>()\n events.value.forEach((event) => {\n if (customerEventService.hasAdditionalInfo(event) && event.event_id) {\n map.set(event.event_id, customerEventService.getTooltipContent(event))\n }\n })\n return map\n})\n\nconst loadEvents = async () => {\n loading.value = true\n error.value = null\n\n try {\n const response = await customerEventService.getMyEvents({\n page: pagination.value.page,\n limit: pagination.value.limit\n })\n\n if (response) {\n if (response.events) {\n events.value = response.events\n }\n\n if (response.page) {\n pagination.value.page = response.page\n }\n\n if (response.limit) {\n pagination.value.limit = response.limit\n }\n\n if (response.total) {\n pagination.value.total = response.total\n }\n\n if (response.totalPages) {\n pagination.value.totalPages = response.totalPages\n }\n\n // Check if a pending top-up has completed\n useTelemetry()?.checkForCompletedTopup(response.events)\n } else {\n error.value = customerEventService.error.value || 'Failed to load events'\n }\n } catch (err) {\n error.value = err instanceof Error ? err.message : 'Unknown error'\n console.error('Error loading events:', err)\n } finally {\n loading.value = false\n }\n}\n\nconst onPageChange = (event: { page: number }) => {\n pagination.value.page = event.page + 1\n loadEvents().catch((error) => {\n console.error('Error loading events:', error)\n })\n}\n\nconst refresh = async () => {\n pagination.value.page = 1\n await loadEvents()\n}\n\ndefineExpose({\n refresh\n})\n</script>\n","<template>\n <div>\n <div v-if=\"loading\" class=\"flex items-center justify-center p-8\">\n <ProgressSpinner />\n </div>\n <div v-else-if=\"error\" class=\"p-4\">\n <Message severity=\"error\" :closable=\"false\">{{ error }}</Message>\n </div>\n <DataTable\n v-else\n :value=\"events\"\n :paginator=\"true\"\n :rows=\"pagination.limit\"\n :total-records=\"pagination.total\"\n :first=\"dataTableFirst\"\n :lazy=\"true\"\n class=\"p-datatable-sm custom-datatable\"\n @page=\"onPageChange\"\n >\n <Column field=\"event_type\" :header=\"$t('credits.eventType')\">\n <template #body=\"{ data }\">\n <Badge\n :value=\"customerEventService.formatEventType(data.event_type)\"\n :severity=\"customerEventService.getEventSeverity(data.event_type)\"\n />\n </template>\n </Column>\n <Column field=\"details\" :header=\"$t('credits.details')\">\n <template #body=\"{ data }\">\n <div class=\"event-details\">\n <!-- Credits Added -->\n <template v-if=\"data.event_type === EventType.CREDIT_ADDED\">\n <div class=\"font-semibold text-green-500\">\n {{ $t('credits.added') }} ${{\n customerEventService.formatAmount(data.params?.amount)\n }}\n </div>\n </template>\n\n <!-- Account Created -->\n <template v-else-if=\"data.event_type === EventType.ACCOUNT_CREATED\">\n <div>{{ $t('credits.accountInitialized') }}</div>\n </template>\n\n <!-- API Usage -->\n <template\n v-else-if=\"data.event_type === EventType.API_USAGE_COMPLETED\"\n >\n <div class=\"flex flex-col gap-1\">\n <div class=\"font-semibold\">\n {{ data.params?.api_name || 'API' }}\n </div>\n <div class=\"text-sm text-smoke-400\">\n {{ $t('credits.model') }}: {{ data.params?.model || '-' }}\n </div>\n </div>\n </template>\n </div>\n </template>\n </Column>\n <Column field=\"createdAt\" :header=\"$t('credits.time')\">\n <template #body=\"{ data }\">\n {{ customerEventService.formatDate(data.createdAt) }}\n </template>\n </Column>\n <Column field=\"params\" :header=\"$t('credits.additionalInfo')\">\n <template #body=\"{ data }\">\n <Button\n v-if=\"customerEventService.hasAdditionalInfo(data)\"\n v-tooltip.top=\"{\n escape: false,\n value: tooltipContentMap.get(data.event_id) || '',\n pt: {\n text: {\n style: {\n width: 'max-content !important'\n }\n }\n }\n }\"\n variant=\"textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('credits.additionalInfo')\"\n >\n <i class=\"pi pi-info-circle\" />\n </Button>\n </template>\n </Column>\n </DataTable>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport Badge from 'primevue/badge'\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Message from 'primevue/message'\nimport ProgressSpinner from 'primevue/progressspinner'\nimport { computed, ref } from 'vue'\n\nimport Button from '@/components/ui/button/Button.vue'\nimport { useTelemetry } from '@/platform/telemetry'\nimport type { AuditLog } from '@/services/customerEventsService'\nimport {\n EventType,\n useCustomerEventsService\n} from '@/services/customerEventsService'\n\nconst events = ref<AuditLog[]>([])\nconst loading = ref(true)\nconst error = ref<string | null>(null)\n\nconst customerEventService = useCustomerEventsService()\n\nconst pagination = ref({\n page: 1,\n limit: 7,\n total: 0,\n totalPages: 0\n})\n\nconst dataTableFirst = computed(\n () => (pagination.value.page - 1) * pagination.value.limit\n)\n\nconst tooltipContentMap = computed(() => {\n const map = new Map<string, string>()\n events.value.forEach((event) => {\n if (customerEventService.hasAdditionalInfo(event) && event.event_id) {\n map.set(event.event_id, customerEventService.getTooltipContent(event))\n }\n })\n return map\n})\n\nconst loadEvents = async () => {\n loading.value = true\n error.value = null\n\n try {\n const response = await customerEventService.getMyEvents({\n page: pagination.value.page,\n limit: pagination.value.limit\n })\n\n if (response) {\n if (response.events) {\n events.value = response.events\n }\n\n if (response.page) {\n pagination.value.page = response.page\n }\n\n if (response.limit) {\n pagination.value.limit = response.limit\n }\n\n if (response.total) {\n pagination.value.total = response.total\n }\n\n if (response.totalPages) {\n pagination.value.totalPages = response.totalPages\n }\n\n // Check if a pending top-up has completed\n useTelemetry()?.checkForCompletedTopup(response.events)\n } else {\n error.value = customerEventService.error.value || 'Failed to load events'\n }\n } catch (err) {\n error.value = err instanceof Error ? err.message : 'Unknown error'\n console.error('Error loading events:', err)\n } finally {\n loading.value = false\n }\n}\n\nconst onPageChange = (event: { page: number }) => {\n pagination.value.page = event.page + 1\n loadEvents().catch((error) => {\n console.error('Error loading events:', error)\n })\n}\n\nconst refresh = async () => {\n pagination.value.page = 1\n await loadEvents()\n}\n\ndefineExpose({\n refresh\n})\n</script>\n","<template>\n <TabPanel value=\"Credits\" class=\"credits-container h-full\">\n <!-- Legacy Design -->\n <div class=\"flex h-full flex-col\">\n <h2 class=\"mb-2 text-2xl font-bold\">\n {{ $t('credits.credits') }}\n </h2>\n\n <Divider />\n\n <div class=\"flex flex-col gap-2\">\n <h3 class=\"text-sm font-medium text-muted\">\n {{ $t('credits.yourCreditBalance') }}\n </h3>\n <div class=\"flex items-center justify-between\">\n <UserCredit text-class=\"text-3xl font-bold\" />\n <Skeleton v-if=\"loading\" width=\"2rem\" height=\"2rem\" />\n <Button\n v-else-if=\"isActiveSubscription\"\n :loading=\"loading\"\n @click=\"handlePurchaseCreditsClick\"\n >\n {{ $t('credits.purchaseCredits') }}\n </Button>\n </div>\n <div class=\"flex flex-row items-center\">\n <Skeleton\n v-if=\"balanceLoading\"\n width=\"12rem\"\n height=\"1rem\"\n class=\"text-xs\"\n />\n <div v-else-if=\"formattedLastUpdateTime\" class=\"text-xs text-muted\">\n {{ $t('credits.lastUpdated') }}: {{ formattedLastUpdateTime }}\n </div>\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('g.refresh')\"\n @click=\"() => authActions.fetchBalance()\"\n >\n <i class=\"pi pi-refresh\" />\n </Button>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between\">\n <h3>{{ $t('credits.activity') }}</h3>\n <Button\n variant=\"muted-textonly\"\n :loading=\"loading\"\n @click=\"handleCreditsHistoryClick\"\n >\n <i class=\"pi pi-arrow-up-right\" />\n {{ $t('credits.invoiceHistory') }}\n </Button>\n </div>\n\n <template v-if=\"creditHistory.length > 0\">\n <div class=\"grow\">\n <DataTable :value=\"creditHistory\" :show-headers=\"false\">\n <Column field=\"title\" :header=\"$t('g.name')\">\n <template #body=\"{ data }\">\n <div class=\"text-sm font-medium\">{{ data.title }}</div>\n <div class=\"text-xs text-muted\">{{ data.timestamp }}</div>\n </template>\n </Column>\n <Column field=\"amount\" :header=\"$t('g.amount')\">\n <template #body=\"{ data }\">\n <div\n :class=\"[\n 'text-center text-base font-medium',\n data.isPositive ? 'text-sky-500' : 'text-red-400'\n ]\"\n >\n {{ data.isPositive ? '+' : '-' }}${{\n formatMetronomeCurrency(data.amount, 'usd')\n }}\n </div>\n </template>\n </Column>\n </DataTable>\n </div>\n </template>\n\n <Divider />\n\n <UsageLogsTable ref=\"usageLogsTableRef\" />\n\n <div class=\"flex flex-row gap-2\">\n <Button variant=\"muted-textonly\" @click=\"handleFaqClick\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('credits.faqs') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleOpenPartnerNodesInfo\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleMessageSupport\">\n <i class=\"pi pi-comments\" />\n {{ $t('credits.messageSupport') }}\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Divider from 'primevue/divider'\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, ref, watch } from 'vue'\n\nimport UserCredit from '@/components/common/UserCredit.vue'\nimport UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport { formatMetronomeCurrency } from '@/utils/formatUtil'\n\ninterface CreditHistoryItemData {\n title: string\n timestamp: string\n amount: number\n isPositive: boolean\n}\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst dialogService = useDialogService()\nconst authStore = useFirebaseAuthStore()\nconst authActions = useFirebaseAuthActions()\nconst commandStore = useCommandStore()\nconst telemetry = useTelemetry()\nconst { isActiveSubscription } = useSubscription()\nconst loading = computed(() => authStore.loading)\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\n\nconst usageLogsTableRef = ref<InstanceType<typeof UsageLogsTable> | null>(null)\n\nconst formattedLastUpdateTime = computed(() =>\n authStore.lastBalanceUpdateTime\n ? authStore.lastBalanceUpdateTime.toLocaleString()\n : ''\n)\n\nwatch(\n () => authStore.lastBalanceUpdateTime,\n (newTime, oldTime) => {\n if (newTime && newTime !== oldTime && usageLogsTableRef.value) {\n usageLogsTableRef.value.refresh()\n }\n }\n)\n\nconst handlePurchaseCreditsClick = () => {\n // Track purchase credits entry from Settings > Credits panel\n useTelemetry()?.trackAddApiCreditButtonClicked()\n dialogService.showTopUpCreditsDialog()\n}\n\nconst handleCreditsHistoryClick = async () => {\n await authActions.accessBillingPortal()\n}\n\nconst handleMessageSupport = async () => {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'credits_panel'\n })\n await commandStore.execute('Comfy.ContactSupport')\n}\n\nconst handleFaqClick = () => {\n window.open(\n buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }),\n '_blank'\n )\n}\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n\nconst creditHistory = ref<CreditHistoryItemData[]>([])\n</script>\n","<template>\n <TabPanel value=\"Credits\" class=\"credits-container h-full\">\n <!-- Legacy Design -->\n <div class=\"flex h-full flex-col\">\n <h2 class=\"mb-2 text-2xl font-bold\">\n {{ $t('credits.credits') }}\n </h2>\n\n <Divider />\n\n <div class=\"flex flex-col gap-2\">\n <h3 class=\"text-sm font-medium text-muted\">\n {{ $t('credits.yourCreditBalance') }}\n </h3>\n <div class=\"flex items-center justify-between\">\n <UserCredit text-class=\"text-3xl font-bold\" />\n <Skeleton v-if=\"loading\" width=\"2rem\" height=\"2rem\" />\n <Button\n v-else-if=\"isActiveSubscription\"\n :loading=\"loading\"\n @click=\"handlePurchaseCreditsClick\"\n >\n {{ $t('credits.purchaseCredits') }}\n </Button>\n </div>\n <div class=\"flex flex-row items-center\">\n <Skeleton\n v-if=\"balanceLoading\"\n width=\"12rem\"\n height=\"1rem\"\n class=\"text-xs\"\n />\n <div v-else-if=\"formattedLastUpdateTime\" class=\"text-xs text-muted\">\n {{ $t('credits.lastUpdated') }}: {{ formattedLastUpdateTime }}\n </div>\n <Button\n variant=\"muted-textonly\"\n size=\"icon-sm\"\n :aria-label=\"$t('g.refresh')\"\n @click=\"() => authActions.fetchBalance()\"\n >\n <i class=\"pi pi-refresh\" />\n </Button>\n </div>\n </div>\n\n <div class=\"flex items-center justify-between\">\n <h3>{{ $t('credits.activity') }}</h3>\n <Button\n variant=\"muted-textonly\"\n :loading=\"loading\"\n @click=\"handleCreditsHistoryClick\"\n >\n <i class=\"pi pi-arrow-up-right\" />\n {{ $t('credits.invoiceHistory') }}\n </Button>\n </div>\n\n <template v-if=\"creditHistory.length > 0\">\n <div class=\"grow\">\n <DataTable :value=\"creditHistory\" :show-headers=\"false\">\n <Column field=\"title\" :header=\"$t('g.name')\">\n <template #body=\"{ data }\">\n <div class=\"text-sm font-medium\">{{ data.title }}</div>\n <div class=\"text-xs text-muted\">{{ data.timestamp }}</div>\n </template>\n </Column>\n <Column field=\"amount\" :header=\"$t('g.amount')\">\n <template #body=\"{ data }\">\n <div\n :class=\"[\n 'text-center text-base font-medium',\n data.isPositive ? 'text-sky-500' : 'text-red-400'\n ]\"\n >\n {{ data.isPositive ? '+' : '-' }}${{\n formatMetronomeCurrency(data.amount, 'usd')\n }}\n </div>\n </template>\n </Column>\n </DataTable>\n </div>\n </template>\n\n <Divider />\n\n <UsageLogsTable ref=\"usageLogsTableRef\" />\n\n <div class=\"flex flex-row gap-2\">\n <Button variant=\"muted-textonly\" @click=\"handleFaqClick\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('credits.faqs') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleOpenPartnerNodesInfo\">\n <i class=\"pi pi-question-circle\" />\n {{ $t('subscription.partnerNodesCredits') }}\n </Button>\n <Button variant=\"muted-textonly\" @click=\"handleMessageSupport\">\n <i class=\"pi pi-comments\" />\n {{ $t('credits.messageSupport') }}\n </Button>\n </div>\n </div>\n </TabPanel>\n</template>\n\n<script setup lang=\"ts\">\nimport Column from 'primevue/column'\nimport DataTable from 'primevue/datatable'\nimport Divider from 'primevue/divider'\nimport Skeleton from 'primevue/skeleton'\nimport TabPanel from 'primevue/tabpanel'\nimport { computed, ref, watch } from 'vue'\n\nimport UserCredit from '@/components/common/UserCredit.vue'\nimport UsageLogsTable from '@/components/dialog/content/setting/UsageLogsTable.vue'\nimport Button from '@/components/ui/button/Button.vue'\nimport { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'\nimport { useExternalLink } from '@/composables/useExternalLink'\nimport { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'\nimport { useTelemetry } from '@/platform/telemetry'\nimport { useDialogService } from '@/services/dialogService'\nimport { useCommandStore } from '@/stores/commandStore'\nimport { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'\nimport { formatMetronomeCurrency } from '@/utils/formatUtil'\n\ninterface CreditHistoryItemData {\n title: string\n timestamp: string\n amount: number\n isPositive: boolean\n}\n\nconst { buildDocsUrl, docsPaths } = useExternalLink()\nconst dialogService = useDialogService()\nconst authStore = useFirebaseAuthStore()\nconst authActions = useFirebaseAuthActions()\nconst commandStore = useCommandStore()\nconst telemetry = useTelemetry()\nconst { isActiveSubscription } = useSubscription()\nconst loading = computed(() => authStore.loading)\nconst balanceLoading = computed(() => authStore.isFetchingBalance)\n\nconst usageLogsTableRef = ref<InstanceType<typeof UsageLogsTable> | null>(null)\n\nconst formattedLastUpdateTime = computed(() =>\n authStore.lastBalanceUpdateTime\n ? authStore.lastBalanceUpdateTime.toLocaleString()\n : ''\n)\n\nwatch(\n () => authStore.lastBalanceUpdateTime,\n (newTime, oldTime) => {\n if (newTime && newTime !== oldTime && usageLogsTableRef.value) {\n usageLogsTableRef.value.refresh()\n }\n }\n)\n\nconst handlePurchaseCreditsClick = () => {\n // Track purchase credits entry from Settings > Credits panel\n useTelemetry()?.trackAddApiCreditButtonClicked()\n dialogService.showTopUpCreditsDialog()\n}\n\nconst handleCreditsHistoryClick = async () => {\n await authActions.accessBillingPortal()\n}\n\nconst handleMessageSupport = async () => {\n telemetry?.trackHelpResourceClicked({\n resource_type: 'help_feedback',\n is_external: true,\n source: 'credits_panel'\n })\n await commandStore.execute('Comfy.ContactSupport')\n}\n\nconst handleFaqClick = () => {\n window.open(\n buildDocsUrl('/tutorials/api-nodes/faq', { includeLocale: true }),\n '_blank'\n )\n}\n\nconst handleOpenPartnerNodesInfo = () => {\n window.open(\n buildDocsUrl(docsPaths.partnerNodesPricing, { includeLocale: true }),\n '_blank'\n )\n}\n\nconst creditHistory = ref<CreditHistoryItemData[]>([])\n</script>\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"./vendor-primevue-DcMRXJN3.js";import"./vendor-other-DlQF6V2E.js";import"./api-Dwq2LQIW.js";import"./remoteConfig-CZcEXsZS.js";import"./colorUtil-CzxntCbX.js";import"./useErrorHandling-Cfa5N_7c.js";import"./Button-Do2I1OAA.js";import"./PanelTemplate-BJda9e5J.js";import"./dialogService-YG0RH337.js";import"./vendor-tiptap-_UqYL7N_.js";import"./vendor-xterm-BU_lcTPR.js";import"./vendor-three-BFcUNSs9.js";import"./markdownRendererUtil-DglHsU8t.js";import"./userStore-BAS9m9W6.js";import{t as L}from"./Load3D-vYr8M3jJ.js";export{L as default};
|