@minimalcorp/tsunagi 0.1.25 → 0.1.27

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.
Files changed (63) hide show
  1. package/.next/standalone/apps/web/.next/BUILD_ID +1 -1
  2. package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
  3. package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
  5. package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
  6. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  7. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  8. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  9. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  10. package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  11. package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
  12. package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +1 -1
  13. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  14. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  15. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  16. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  17. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  18. package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  19. package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
  20. package/.next/standalone/apps/web/.next/server/app/index.rsc +2 -2
  21. package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  22. package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
  23. package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  24. package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +1 -1
  25. package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  26. package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
  27. package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
  29. package/.next/standalone/apps/web/.next/server/app/settings.rsc +2 -2
  30. package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +2 -2
  31. package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
  32. package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +1 -1
  33. package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -1
  34. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
  35. package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
  36. package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/apps/web/.next/server/chunks/ssr/_03hz06e._.js +1 -1
  38. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0836rl_._.js +1 -1
  39. package/.next/standalone/apps/web/.next/server/chunks/ssr/_0amxree._.js +1 -1
  40. package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_settings_page_tsx_0i6itlb._.js +1 -1
  41. package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
  42. package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
  43. package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
  44. package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
  45. package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
  46. package/.next/standalone/apps/web/.next/static/chunks/{0xxgdzi2vipaj.js → 0.1pdl3yo~xet.js} +1 -1
  47. package/.next/standalone/apps/web/.next/static/chunks/001oo2jah4cv9.js +1 -0
  48. package/.next/standalone/apps/web/.next/static/chunks/0nhpg19g1rl_7.js +1 -0
  49. package/.next/standalone/apps/web/.next/static/chunks/164p.6qr5~-7x.js +1 -0
  50. package/dist/server/lib/llm-process.js +24 -7
  51. package/dist/server/lib/voice-debug-log.d.ts +9 -0
  52. package/dist/server/lib/voice-debug-log.js +28 -0
  53. package/dist/server/lib/whisper-process.js +22 -5
  54. package/dist/server/routes/whisper.js +30 -5
  55. package/npm-shrinkwrap.json +528 -157
  56. package/package.json +1 -1
  57. package/whisper-server/server.py +2 -1
  58. package/.next/standalone/apps/web/.next/static/chunks/0592-patkjl3_.js +0 -1
  59. package/.next/standalone/apps/web/.next/static/chunks/0915n6rwhsagp.js +0 -1
  60. package/.next/standalone/apps/web/.next/static/chunks/0922av8h192l7.js +0 -1
  61. /package/.next/standalone/apps/web/.next/static/{qMzGt_O2oG-7wQXWYfQUB → pTO1DUFCsjrTTtmSPyfRJ}/_buildManifest.js +0 -0
  62. /package/.next/standalone/apps/web/.next/static/{qMzGt_O2oG-7wQXWYfQUB → pTO1DUFCsjrTTtmSPyfRJ}/_clientMiddlewareManifest.js +0 -0
  63. /package/.next/standalone/apps/web/.next/static/{qMzGt_O2oG-7wQXWYfQUB → pTO1DUFCsjrTTtmSPyfRJ}/_ssgManifest.js +0 -0
@@ -0,0 +1 @@
1
+ (globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,18566,(e,t,r)=>{t.exports=e.r(76562)},43531,78745,e=>{"use strict";let t=(0,e.i(75254).default)("check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);e.s(["default",0,t],78745),e.s(["Check",0,t],43531)},86536,e=>{"use strict";let t=(0,e.i(75254).default)("eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);e.s(["Eye",0,t],86536)},27612,e=>{"use strict";let t=(0,e.i(75254).default)("trash-2",[["path",{d:"M10 11v6",key:"nco0om"}],["path",{d:"M14 11v6",key:"outv1u"}],["path",{d:"M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",key:"miytrc"}],["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",key:"e791ji"}]]);e.s(["Trash2",0,t],27612)},10012,24564,94349,e=>{"use strict";var t,r=e.i(18050);e.i(47167);var a=e.i(71645);e.s([],19485),e.i(19485);var i=e.i(76477),n=e.i(67805),s=e.i(17514);let l=((t={}).disabled="data-disabled",t.valid="data-valid",t.invalid="data-invalid",t.touched="data-touched",t.dirty="data-dirty",t.filled="data-filled",t.focused="data-focused",t),o={badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:null,valueMissing:!1},d={valid:null,touched:!1,dirty:!1,filled:!1,focused:!1},u={disabled:!1,...d},c={valid:e=>null===e?null:e?{[l.valid]:""}:{[l.invalid]:""}},f=a.createContext({invalid:void 0,name:void 0,validityData:{state:o,errors:[],error:"",value:"",initialValue:null},setValidityData:s.NOOP,disabled:void 0,touched:d.touched,setTouched:s.NOOP,dirty:d.dirty,setDirty:s.NOOP,filled:d.filled,setFilled:s.NOOP,focused:d.focused,setFocused:s.NOOP,validate:()=>null,validationMode:"onSubmit",validationDebounceTime:0,shouldValidateOnChange:()=>!1,state:u,markedDirtyRef:{current:!1},validation:{getValidationProps:(e=s.EMPTY_OBJECT)=>e,getInputValidationProps:(e=s.EMPTY_OBJECT)=>e,inputRef:{current:null},commit:async()=>{}}});function m(e=!0){let t=a.useContext(f);if(t.setValidityData===s.NOOP&&!e)throw Error((0,n.default)(28));return t}let p=a.createContext({legendId:void 0,setLegendId:()=>{},disabled:void 0}),v=a.createContext({formRef:{current:{fields:new Map}},errors:{},clearErrors:s.NOOP,validationMode:"onSubmit",submitAttemptedRef:{current:!1}});function g(){return a.useContext(v)}var x=e.i(30628),h=e.i(19805),b=e.i(89879);let y=a.createContext({controlId:void 0,registerControlId:s.NOOP,labelId:void 0,setLabelId:s.NOOP,messageIds:[],setMessageIds:s.NOOP,getDescriptionProps:e=>e});function j(){return a.useContext(y)}let M=function(e){let t=(0,b.useBaseUiId)(),n=void 0===e.controlId?t:e.controlId,[s,l]=a.useState(n),[o,d]=a.useState(e.labelId),[u,c]=a.useState([]),f=(0,x.useRefWithInit)(()=>new Map),{messageIds:m}=j(),p=(0,i.useStableCallback)((e,t)=>{let r=f.current;void 0===t?r.delete(e):(r.set(e,t),l(e=>{let t;if(0!==r.size){for(let a of r.values()){if(void 0!==e&&a===e)return e;void 0===t&&(t=a)}return t}}))}),v=a.useCallback(e=>(0,h.mergeProps)({"aria-describedby":m.concat(u).join(" ")||void 0},e),[m,u]),g=a.useMemo(()=>({controlId:s,registerControlId:p,labelId:o,setLabelId:d,messageIds:u,setMessageIds:c,getDescriptionProps:v}),[s,p,o,d,u,c,v]);return(0,r.jsx)(y.Provider,{value:g,children:e.children})};var w=e.i(27601),C=e.i(66745);function k(e,t){return{...e,state:{...e.state,valid:!t&&e.state.valid}}}let N=Object.keys(o),S=a.forwardRef(function(e,t){let{errors:l,validationMode:d,submitAttemptedRef:u}=g(),{render:m,className:v,validate:x,validationDebounceTime:b=0,validationMode:y=d,name:M,disabled:S=!1,invalid:E,dirty:L,touched:R,actionsRef:I,...T}=e,{disabled:P}=function(e=!1){let t=a.useContext(p);if(!t&&!e)throw Error((0,n.default)(86));return t}(),O=(0,i.useStableCallback)(x||(()=>null)),B=P||S,[A,V]=a.useState(!1),[_,z]=a.useState(!1),[D,F]=a.useState(!1),[U,H]=a.useState(!1),$=L??_,q=R??A,G=a.useRef(!1),Y=(0,i.useStableCallback)(e=>{void 0===L&&(e&&(G.current=!0),z(e))}),J=(0,i.useStableCallback)(e=>{void 0===R&&V(e)}),K=(0,i.useStableCallback)(()=>"onChange"===y||"onSubmit"===y&&u.current),W=!!M&&Object.hasOwn(l,M)&&void 0!==l[M],Q=!0===E||W,[X,Z]=a.useState({state:o,error:"",errors:[],value:null,initialValue:null}),ee=!Q&&X.state.valid,et=a.useMemo(()=>({disabled:B,touched:q,dirty:$,valid:ee,filled:D,focused:U}),[B,q,$,ee,D,U]),er=function(e){let{formRef:t,clearErrors:r}=g(),{setValidityData:n,validate:l,validityData:d,validationDebounceTime:u,invalid:c,markedDirtyRef:f,state:m,name:p,shouldValidateOnChange:v}=e,{controlId:x,getDescriptionProps:b}=j(),y=(0,C.useTimeout)(),M=a.useRef(null),w=(0,i.useStableCallback)(async(e,r=!1)=>{let a,i=M.current;if(!i)return;if(r){if(!1!==m.valid)return;let r=i.validity;if(!r.valueMissing){let r={value:e,state:{...o,valid:!0},error:"",errors:[],initialValue:d.initialValue};if(i.setCustomValidity(""),x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(r,!1)})}n(r);return}let a=N.reduce((e,t)=>(e[t]=r[t],e),{});if(!a.valid&&!function(e){if(!e||e.valid||!e.valueMissing)return!1;let t=!1;for(let r of N)"valid"!==r&&("valueMissing"===r&&(t=e[r]),e[r]&&(t=!1));return t}(a))return}y.clear();let s=null,u=[],p=function(e){let t=N.reduce((t,r)=>(t[r]=e.validity[r],t),{}),r=!1;for(let e of N)if("valid"!==e){if("valueMissing"===e&&t[e])r=!0;else if(t[e])return t}return r&&!f.current&&(t.valid=!0,t.valueMissing=!1),t}(i),g=v();if(i.validationMessage&&!g)a=i.validationMessage,u=[i.validationMessage];else{let r=l(e,Array.from(t.current.fields.values()).reduce((e,t)=>(t.name&&(e[t.name]=t.getValue()),e),{}));null!==(s="object"==typeof r&&null!==r&&"then"in r?await r:r)?(p.valid=!1,p.customError=!0,Array.isArray(s)?(u=s,i.setCustomValidity(s.join("\n"))):s&&(u=[s],i.setCustomValidity(s))):g&&(i.setCustomValidity(""),p.customError=!1,i.validationMessage?(a=i.validationMessage,u=[i.validationMessage]):i.validity.valid&&!p.valid&&(p.valid=!0))}let h={value:e,state:p,error:a??(Array.isArray(s)?s[0]:s??""),errors:u,initialValue:d.initialValue};if(x){let e=t.current.fields.get(x);e&&t.current.fields.set(x,{...e,...k(h,c)})}n(h)}),S=a.useCallback((e={})=>(0,h.mergeProps)(b,!1===m.valid?{"aria-invalid":!0}:s.EMPTY_OBJECT,e),[b,m.valid]),E=a.useCallback((e={})=>(0,h.mergeProps)({onChange(e){if(e.nativeEvent.defaultPrevented)return;if(r(p),!v())return void w(e.currentTarget.value,!0);let t=e.currentTarget;""===t.value?w(t.value):(y.clear(),u?y.start(u,()=>{w(t.value)}):w(t.value))}},S(e)),[S,r,p,y,w,u,v]);return a.useMemo(()=>({getValidationProps:S,getInputValidationProps:E,inputRef:M,commit:w}),[S,E,w])}({setValidityData:Z,validate:O,validityData:X,validationDebounceTime:b,invalid:Q,markedDirtyRef:G,state:et,name:M,shouldValidateOnChange:K}),ea=a.useCallback(()=>{G.current=!0,er.commit(X.value)},[er,X]);a.useImperativeHandle(I,()=>({validate:ea}),[ea]);let ei=a.useMemo(()=>({invalid:Q,name:M,validityData:X,setValidityData:Z,disabled:B,touched:q,setTouched:J,dirty:$,setDirty:Y,filled:D,setFilled:F,focused:U,setFocused:H,validate:O,validationMode:y,validationDebounceTime:b,shouldValidateOnChange:K,state:et,markedDirtyRef:G,validation:er}),[Q,M,X,B,q,J,$,Y,D,F,U,H,O,y,b,K,et,er]),en=(0,w.useRenderElement)("div",e,{ref:t,state:et,props:T,stateAttributesMapping:c});return(0,r.jsx)(f.Provider,{value:ei,children:en})}),E=a.forwardRef(function(e,t){return(0,r.jsx)(M,{children:(0,r.jsx)(S,{...e,ref:t})})});var L=e.i(29315),R=e.i(90219),I=e.i(47857),T=e.i(977);function P(e,t){let r=(0,b.useBaseUiId)(e);return(0,T.useIsoLayoutEffect)(()=>(t(r),()=>{t(void 0)}),[r,t]),r}e.s(["useRegisteredLabelId",0,P],24564);let O=a.forwardRef(function(e,t){let{render:r,className:n,id:s,nativeLabel:l=!0,...o}=e,d=m(!1),{labelId:u}=j(),f=a.useRef(null),p=function(e={}){let{id:t,fallbackControlId:r,native:a=!1,setLabelId:n,focusControl:s}=e,{controlId:l,setLabelId:o}=j(),d=P(t,(0,i.useStableCallback)(e=>{o(e),n?.(e)})),u=l??r;function c(e){let t=(0,I.getTarget)(e.nativeEvent);t?.closest("button,input,select,textarea")||(!e.defaultPrevented&&e.detail>1&&e.preventDefault(),a||function(e){if(s)return s(e,u);if(!u)return;let t=(0,R.ownerDocument)(e.currentTarget).getElementById(u);(0,L.isHTMLElement)(t)&&t.focus({focusVisible:!0})}(e))}return a?{id:d,htmlFor:u??void 0,onMouseDown:c}:{id:d,onClick:c,onPointerDown(e){e.preventDefault()}}}({id:u??s,native:l});return(0,w.useRenderElement)("label",e,{ref:[t,f],state:d.state,props:[p,o],stateAttributesMapping:c})});var B=e.i(89269),A=e.i(84759),V=e.i(88841);let _={...c,...A.transitionStatusMapping},z=a.forwardRef(function(e,t){let{render:i,id:n,className:s,match:l,...o}=e,d=(0,b.useBaseUiId)(n),{validityData:u,state:c,name:f}=m(!1),{setMessageIds:p}=j(),{errors:v}=g(),x=f?v[f]:null,h=!1;h=!!x||!0===l||(l?!!u.state[l]:!1===u.state.valid);let{mounted:y,transitionStatus:M,setMounted:C}=(0,V.useTransitionStatus)(h);(0,T.useIsoLayoutEffect)(()=>{if(h&&d)return p(e=>e.concat(d)),()=>{p(e=>e.filter(e=>e!==d))}},[h,d,p]);let k=a.useRef(null),[N,S]=a.useState(null),[E,L]=a.useState(null),R=x||(u.errors.length>1?(0,r.jsx)("ul",{children:u.errors.map(e=>(0,r.jsx)("li",{children:e},e))}):u.error),I=u.error;null!=x?I=Array.isArray(x)?JSON.stringify(x):x:u.errors.length>1&&(I=JSON.stringify(u.errors)),h&&I!==E&&(L(I),S(R)),(0,B.useOpenChangeComplete)({open:h,ref:k,onComplete(){h||C(!1)}});let P={...c,transitionStatus:M},O=(0,w.useRenderElement)("div",e,{ref:[t,k],state:P,props:[{id:d,children:h?R:N},o],stateAttributesMapping:_,enabled:y});return y?O:null}),D=a.forwardRef(function(e,t){let{render:r,id:a,className:i,...n}=e,s=(0,b.useBaseUiId)(a),l=m(!1),{setMessageIds:o}=j();return(0,T.useIsoLayoutEffect)(()=>{if(s)return o(e=>e.concat(s)),()=>{o(e=>e.filter(e=>e!==s))}},[s,o]),(0,w.useRenderElement)("p",e,{ref:t,state:l.state,props:[{id:s},n],stateAttributesMapping:c})});function F({controlled:e,default:t,name:r,state:i="value"}){let{current:n}=a.useRef(void 0!==e),[s,l]=a.useState(t),o=a.useCallback(e=>{n||l(e)},[]);return[n?e:s,o]}e.s(["useControlled",0,F],94349);var U=e.i(74080),H=e.i(5963),$=e.i(3404);let q=a.forwardRef(function(e,t){let{render:r,className:n,id:l,name:o,value:d,disabled:u=!1,onValueChange:f,defaultValue:p,autoFocus:v=!1,...h}=e,{state:y,name:M,disabled:C,setTouched:N,setDirty:S,validityData:E,setFocused:P,setFilled:O,validationMode:B,validation:A}=m(),V=C||u,_=M??o,z={...y,disabled:V},{labelId:D}=j(),q=function(e={}){let{id:t,implicit:r=!1,controlRef:n}=e,{controlId:l,registerControlId:o}=j(),d=(0,b.useBaseUiId)(t),u=r?l:void 0,c=(0,x.useRefWithInit)(()=>Symbol("labelable-control")),f=a.useRef(!1),m=a.useRef(null!=t),p=(0,i.useStableCallback)(()=>{f.current&&o!==s.NOOP&&(f.current=!1,o(c.current,void 0))});return(0,T.useIsoLayoutEffect)(()=>{let e;if(o!==s.NOOP){if(r){let r=n?.current;e=(0,L.isElement)(r)&&null!=r.closest("label")?t??null:u??d}else if(null!=t)m.current=!0,e=t;else{if(!m.current)return void p();e=d}if(void 0===e)return void p();f.current=!0,o(c.current,e)}},[t,n,u,o,r,d,c,p]),a.useEffect(()=>p,[p]),l??d}({id:l});(0,T.useIsoLayoutEffect)(()=>{let e=null!=d;A.inputRef.current?.value||e&&""!==d?O(!0):e&&""===d&&O(!1)},[A.inputRef,O,d]);let G=a.useRef(null);(0,T.useIsoLayoutEffect)(()=>{v&&G.current===(0,I.activeElement)((0,R.ownerDocument)(G.current))&&P(!0)},[v,P]);let[Y]=F({controlled:d,default:p,name:"FieldControl",state:"value"}),J=void 0!==d,K=J?Y:void 0;return!function(e){let{enabled:t=!0,value:r,id:a,name:n,controlRef:s,commit:l}=e,{formRef:o}=g(),{invalid:d,markedDirtyRef:u,validityData:c,setValidityData:f}=m(),p=(0,i.useStableCallback)(e.getValue);(0,T.useIsoLayoutEffect)(()=>{if(!t)return;let e=r;void 0===e&&(e=p()),null===c.initialValue&&null!==e&&f(t=>({...t,initialValue:e}))},[t,f,r,c.initialValue,p]),(0,T.useIsoLayoutEffect)(()=>{t&&a&&o.current.fields.set(a,{getValue:p,name:n,controlRef:s,validityData:k(c,d),validate(e=!0){let t=r;void 0===t&&(t=p()),u.current=!0,e?U.flushSync(()=>l(t)):l(t)}})},[l,s,t,o,p,a,d,u,n,c,r]),(0,T.useIsoLayoutEffect)(()=>{let e=o.current.fields;return()=>{a&&e.delete(a)}},[o,a])}({id:q,name:_,commit:A.commit,value:K,getValue:()=>A.inputRef.current?.value,controlRef:A.inputRef}),(0,w.useRenderElement)("input",e,{ref:[t,G],state:z,props:[{id:q,disabled:V,name:_,ref:A.inputRef,"aria-labelledby":D,autoFocus:v,...J?{value:K}:{defaultValue:p},onChange(e){let t=e.currentTarget.value;f?.(t,(0,H.createChangeEventDetails)($.REASONS.none,e.nativeEvent)),S(t!==E.initialValue),O(""!==t)},onFocus(){P(!0)},onBlur(e){N(!0),P(!1),"onBlur"===B&&A.commit(e.currentTarget.value)},onKeyDown(e){"INPUT"===e.currentTarget.tagName&&"Enter"===e.key&&(N(!0),A.commit(e.currentTarget.value))}},A.getInputValidationProps(),h],stateAttributesMapping:c})}),G=a.createContext({disabled:!1}),Y=a.createContext(void 0),J=a.forwardRef(function(e,t){let{render:i,className:s,disabled:l=!1,...o}=e,{state:d,disabled:u}=m(!1),f=u||l,p=function(e=!0){let t=a.useContext(Y);if(void 0===t&&!e)throw Error((0,n.default)(3));return t}(),v=p?.parent.id,g=p?.allValues!==void 0,x=a.useMemo(()=>({disabled:f}),[f]),h=(0,w.useRenderElement)("div",e,{ref:t,state:d,props:o,stateAttributesMapping:c});return(0,r.jsx)(M,{controlId:g?v:void 0,children:(0,r.jsx)(G.Provider,{value:x,children:h})})});e.s(["Control",0,q,"Description",0,D,"Error",0,z,"Item",0,J,"Label",0,O,"Root",0,E,"Validity",0,function(e){let{children:t}=e,{validityData:i,invalid:n}=m(!1),s=a.useMemo(()=>k(i,n),[i,n]),l=!1===s.state.valid,{transitionStatus:o}=(0,V.useTransitionStatus)(l),d=a.useMemo(()=>({...s,validity:s.state,transitionStatus:o}),[s,o]);return(0,r.jsx)(a.Fragment,{children:t(d)})}],71975);var K=e.i(71975),K=K;let W=a.forwardRef(function(e,t){return(0,r.jsx)(K.Control,{ref:t,...e})});var Q=e.i(52088);e.s(["Input",0,function({className:e,type:t,...a}){return(0,r.jsx)(W,{type:t,"data-slot":"input",className:(0,Q.cn)("h-9 w-full min-w-0 rounded-md border border-input bg-input/20 px-3 py-1 text-sm transition-colors outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}],10012)},37870,31278,e=>{"use strict";var t=e.i(18050),r=e.i(17521);e.s(["Loader2",()=>r.default],31278);var r=r;e.s(["LoadingSpinner",0,function({size:e="md",message:a}){return(0,t.jsxs)("div",{className:"flex flex-col items-center justify-center gap-3",children:[(0,t.jsx)(r.default,{className:`${{sm:"w-4 h-4",md:"w-8 h-8",lg:"w-12 h-12"}[e]} text-primary animate-spin`}),a&&(0,t.jsx)("p",{className:"text-sm text-muted-foreground",children:a})]})}],37870)},63209,e=>{"use strict";let t=(0,e.i(75254).default)("circle-alert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);e.s(["AlertCircle",0,t],63209)},95468,e=>{"use strict";var t=e.i(23287);e.s(["CheckCircle2",()=>t.default])},58e3,e=>{"use strict";let t=(0,e.i(75254).default)("circle-question-mark",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3",key:"1u773s"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);e.s(["CircleHelp",0,t],58e3)},87951,52044,e=>{"use strict";var t=e.i(75254);let r=(0,t.default)("mic",[["path",{d:"M12 19v3",key:"npa21l"}],["path",{d:"M19 10v2a7 7 0 0 1-14 0v-2",key:"1vc78b"}],["rect",{x:"9",y:"2",width:"6",height:"13",rx:"3",key:"s6n7sd"}]]);e.s(["Mic",0,r],87951);let a=(0,t.default)("square",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}]]);e.s(["Square",0,a],52044)},64036,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Card",0,function({className:e,size:a="default",...i}){return(0,t.jsx)("div",{"data-slot":"card","data-size":a,className:(0,r.cn)("group/card flex flex-col gap-4 overflow-hidden rounded-lg bg-card py-4 text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg",e),...i})},"CardContent",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-content",className:(0,r.cn)("px-4 group-data-[size=sm]/card:px-3",e),...a})},"CardDescription",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-description",className:(0,r.cn)("text-xs/relaxed text-muted-foreground",e),...a})},"CardHeader",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-header",className:(0,r.cn)("group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-4 group-data-[size=sm]/card:px-3 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",e),...a})},"CardTitle",0,function({className:e,...a}){return(0,t.jsx)("div",{"data-slot":"card-title",className:(0,r.cn)("font-heading text-sm font-medium",e),...a})}])},77814,e=>{"use strict";var t,r=e.i(18050);e.s([],33847),e.i(33847),e.i(47167);var a=e.i(71645),i=e.i(231),n=e.i(62372);let s=new Map;function l(e,t,r){return null==e?"":(function(e,t){let r=JSON.stringify({locale:e,options:t}),a=s.get(r);if(a)return a;let i=new Intl.NumberFormat(e,t);return s.set(r,i),i})(t,r).format(e)}var o=e.i(27601),d=e.i(67805);let u=a.createContext(void 0);function c(){let e=a.useContext(u);if(void 0===e)throw Error((0,d.default)(51));return e}let f=((t={}).complete="data-complete",t.indeterminate="data-indeterminate",t.progressing="data-progressing",t),m={status:e=>"progressing"===e?{[f.progressing]:""}:"complete"===e?{[f.complete]:""}:"indeterminate"===e?{[f.indeterminate]:""}:null};function p(e,t){return null==t?"indeterminate progress":e||`${t}%`}let v=a.forwardRef(function(e,t){var s;let{format:d,getAriaValueText:c=p,locale:f,max:v=100,min:g=0,value:x,render:h,className:b,children:y,...j}=e,[M,w]=a.useState(),C=(0,i.useValueAsRef)(d),k="indeterminate";Number.isFinite(x)&&(k=x===v?"complete":"progressing");let N=(s=C.current,null==x?"":s?l(x,f,s):l(x/100,f,{style:"percent"})),S=a.useMemo(()=>({status:k}),[k]),E={"aria-labelledby":M,"aria-valuemax":v,"aria-valuemin":g,"aria-valuenow":x??void 0,"aria-valuetext":c(N,x),role:"progressbar",children:(0,r.jsxs)(a.Fragment,{children:[y,(0,r.jsx)("span",{role:"presentation",style:n.visuallyHidden,children:"x"})]})},L=a.useMemo(()=>({formattedValue:N,max:v,min:g,setLabelId:w,state:S,status:k,value:x}),[N,v,g,w,S,k,x]),R=(0,o.useRenderElement)("div",e,{state:S,ref:t,props:[E,j],stateAttributesMapping:m});return(0,r.jsx)(u.Provider,{value:L,children:R})}),g=a.forwardRef(function(e,t){let{render:r,className:a,...i}=e,{state:n}=c();return(0,o.useRenderElement)("div",e,{state:n,ref:t,props:i,stateAttributesMapping:m})}),x=a.forwardRef(function(e,t){let{render:r,className:i,...n}=e,{max:s,min:l,value:d,state:u}=c(),f=Number.isFinite(d)&&null!==d?(d-l)*100/(s-l):null,p=a.useCallback(()=>null==f?{}:{insetInlineStart:0,height:"inherit",width:`${f}%`},[f]);return(0,o.useRenderElement)("div",e,{state:u,ref:t,props:[{style:p()},n],stateAttributesMapping:m})}),h=a.forwardRef(function(e,t){let{className:r,render:a,children:i,...n}=e,{value:s,formattedValue:l,state:d}=c(),u=null==s?"indeterminate":l,f=null==s?null:l;return(0,o.useRenderElement)("span",e,{state:d,ref:t,props:[{"aria-hidden":!0,children:"function"==typeof i?i(u,s):f},n],stateAttributesMapping:m})});var b=e.i(24564);let y=a.forwardRef(function(e,t){let{render:r,className:a,id:i,...n}=e,{setLabelId:s,state:l}=c(),d=(0,b.useRegisteredLabelId)(i,s);return(0,o.useRenderElement)("span",e,{state:l,ref:t,props:[{id:d,role:"presentation"},n],stateAttributesMapping:m})});e.s(["Indicator",0,x,"Label",0,y,"Root",0,v,"Track",0,g,"Value",0,h],45929);var j=e.i(45929),j=j,M=e.i(52088);function w({className:e,...t}){return(0,r.jsx)(j.Track,{className:(0,M.cn)("relative flex h-1 w-full items-center overflow-x-hidden rounded-md bg-muted",e),"data-slot":"progress-track",...t})}function C({className:e,...t}){return(0,r.jsx)(j.Indicator,{"data-slot":"progress-indicator",className:(0,M.cn)("h-full bg-primary",e),style:{transition:"width 500ms ease-in-out"},...t})}e.s(["Progress",0,function({className:e,children:t,value:a,...i}){return(0,r.jsxs)(j.Root,{value:a,"data-slot":"progress",className:(0,M.cn)("flex flex-wrap gap-3",e),...i,children:[t,(0,r.jsx)(w,{children:(0,r.jsx)(C,{})})]})}],77814)},41247,e=>{"use strict";var t=e.i(18050),r=e.i(52088);e.s(["Textarea",0,function({className:e,...a}){return(0,t.jsx)("textarea",{"data-slot":"textarea",className:(0,r.cn)("flex field-sizing-content min-h-16 w-full resize-none rounded-md border border-input bg-input/20 px-2 py-2 text-sm transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-xs/relaxed dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",e),...a})}])},57024,e=>{"use strict";var t=e.i(85699);function r(e){let{type:r,title:a,description:i,duration:n}=e,s=e.id??crypto.randomUUID(),l={id:s,description:i,duration:n};switch(r){case"loading":t.toast.loading(a,l);break;case"success":t.toast.success(a,l);break;case"error":t.toast.error(a,l);break;case"warning":t.toast.warning(a,l);break;case"info":t.toast.info(a,l);break;default:(0,t.toast)(a,l)}return s}e.s(["toaster",0,{create:e=>r(e),update:(e,t)=>{r({...t,id:e})},dismiss:e=>{t.toast.dismiss(e)}}])},31245,e=>{"use strict";let t=(0,e.i(75254).default)("bot",[["path",{d:"M12 8V4H8",key:"hb8ula"}],["rect",{width:"16",height:"12",x:"4",y:"8",rx:"2",key:"enze0r"}],["path",{d:"M2 14h2",key:"vft8re"}],["path",{d:"M20 14h2",key:"4cs60a"}],["path",{d:"M15 13v2",key:"1xurst"}],["path",{d:"M9 13v2",key:"rq6x2g"}]]);e.s(["Bot",0,t],31245)},31449,66961,e=>{"use strict";e.s(["LLM_SYSTEM_PROMPT_STORAGE_KEY",()=>k,"VoiceInputButton",()=>S,"WHISPER_PROMPT_STORAGE_KEY",()=>C],31449);var t=e.i(18050),r=e.i(71645),a=e.i(31278),i=e.i(87951),n=e.i(52044),s=e.i(85902),l=e.i(70935),o=e.i(57024);e.s(["LOCAL_LLM_ENABLED_STORAGE_KEY",()=>h,"LocalLlmSection",()=>w],66961);var d=e.i(63209),u=e.i(31245),c=e.i(95468),f=e.i(58e3),m=e.i(64036),p=e.i(68995),v=e.i(77814),g=e.i(41247);let x="tsunagi:local-llm-enabled",h=x,b={not_running:"停止中",installing_deps:"依存関係をインストール中...",downloading_model:"モデルをダウンロード中...",starting_server:"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)",running:"実行中",running_external:"実行中 (tsunagi外で起動)",error:"エラー"},y=["installing_deps","downloading_model","starting_server"],j=["running","running_external"];function M(e){let t=e/0x40000000;return t>=.1?`${t.toFixed(2)} GB`:`${(e/1048576).toFixed(0)} MB`}function w(){let[e,i]=(0,r.useState)(!1),[h,w]=(0,r.useState)(!1),[C,N]=(0,r.useState)(null),[S,E]=(0,r.useState)(""),L=(0,r.useRef)(null),R=(0,r.useCallback)(e=>{i(e),localStorage.setItem(x,String(e))},[]),I=(0,r.useCallback)(e=>{E(e),localStorage.setItem(k,e)},[]),T=(0,r.useCallback)(async()=>{let e=await fetch((0,l.apiUrl)("/api/llm/server/status")),t=await e.json();return N(t),t},[]);(0,r.useEffect)(()=>{i("true"===localStorage.getItem(x)),E(localStorage.getItem(k)??""),T()},[T]),(0,r.useEffect)(()=>{C&&j.includes(C.step)&&!e&&(R(!0),o.toaster.create({type:"success",title:"ローカルLLMが有効化されました"}))},[C,e,R]),(0,r.useEffect)(()=>(C&&y.includes(C.step)&&!L.current&&(L.current=setInterval(()=>{T().then(e=>{!y.includes(e.step)&&L.current&&(clearInterval(L.current),L.current=null)})},1e3)),()=>{L.current&&(clearInterval(L.current),L.current=null)}),[C,T]);let P=(0,r.useCallback)(()=>{T(),w(!0)},[T]),O=(0,r.useCallback)(async()=>{try{let e=await fetch((0,l.apiUrl)("/api/llm/server/start"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}N(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの起動に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),B=(0,r.useCallback)(async()=>{try{let e=await fetch((0,l.apiUrl)("/api/llm/server/stop"),{method:"POST"});if(!e.ok){let t=await e.json().catch(()=>null);throw Error(t?.error||`HTTPエラー: ${e.status}`)}N(await e.json())}catch(e){o.toaster.create({type:"error",title:"サーバーの停止に失敗しました",description:e instanceof Error?e.message:String(e)})}},[]),A=(0,r.useCallback)(()=>{R(!1),C&&j.includes(C.step)&&B()},[R,B,C]),V=C?.serverDir,_=C?.downloadProgress,z=_?_.downloadedBytes/_.totalBytes*100:0,D=null!==C,F=D&&j.includes(C.step);return(0,t.jsxs)(m.Card,{children:[(0,t.jsxs)(m.CardHeader,{children:[(0,t.jsxs)("div",{className:"flex items-center gap-1",children:[(0,t.jsx)(m.CardTitle,{children:"ローカルLLM (実験的機能)"}),(0,t.jsx)(s.Button,{variant:"ghost",size:"icon-sm",onClick:P,title:"ローカルLLMについて",children:(0,t.jsx)(f.CircleHelp,{})})]}),(0,t.jsxs)(m.CardDescription,{children:["ローカルで動作するLLM(Qwen3-30B-A3B, MoE)を利用します。",(0,t.jsx)("strong",{className:"font-medium text-foreground",children:"有効にすると、音声入力の文字起こし結果がこのLLMで自動整形されるようになります"}),"(無効時は文字起こし結果をそのまま使用)。"]})]}),(0,t.jsx)(m.CardContent,{className:"flex flex-col gap-3",children:e?(0,t.jsxs)(t.Fragment,{children:[(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[D?F?(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(c.CheckCircle2,{className:"size-4"}),"ローカルLLM: 有効"]}):(0,t.jsxs)("span",{className:"flex items-center gap-2 text-warning",children:[(0,t.jsx)(d.AlertCircle,{className:"size-4"}),"ローカルLLM: 有効化済み(サーバー停止中)"]}):(0,t.jsxs)("span",{className:"flex items-center gap-2 text-muted-foreground",children:[(0,t.jsx)(a.Loader2,{className:"size-4 animate-spin"}),"状態を確認中..."]}),C&&(0,t.jsxs)("span",{className:"text-xs/relaxed text-muted-foreground",children:["(",b[C.step],")"]}),D&&!F&&(0,t.jsxs)(s.Button,{size:"default",onClick:P,children:[(0,t.jsx)(u.Bot,{}),"サーバーを起動"]}),(0,t.jsx)(s.Button,{size:"default",variant:"outline",onClick:A,children:"無効にする"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("label",{className:"mb-1 block text-xs font-medium text-muted-foreground",children:"整形システムプロンプト (任意)"}),(0,t.jsx)(g.Textarea,{value:S,onChange:e=>I(e.target.value),placeholder:"例: 句読点の追加と誤字修正のみ行い、言い回しは変えないでください。",className:"min-h-16 text-xs"}),(0,t.jsx)("p",{className:"mt-1 text-[0.65rem] text-muted-foreground",children:"文字起こし結果をLLMで整形する際の指示です。空欄の場合は既定のプロンプト(句読点の追加・誤字修正のみを行う指示)を使用します。"})]})]}):(0,t.jsxs)(s.Button,{size:"default",onClick:P,children:[(0,t.jsx)(u.Bot,{}),"ローカルLLMを有効化する"]})}),(0,t.jsx)(p.Dialog,{open:h,onOpenChange:({open:e})=>w(e),title:"ローカルLLMについて",maxWidth:"2xl",children:(0,t.jsxs)("div",{className:"space-y-4 text-sm",children:[(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"実験的機能です"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"音声入力の文字起こし結果を、ローカルで動作するLLM(mlx-lm / Qwen3-30B-A3B, MoE構成)で整形します。MoE構成のため実計算に使うアクティブパラメータは約3Bで、denseな同規模モデルより高速に動作します。精度・速度は環境に依存し、今後変更される可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"対応環境"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"Apple Silicon Mac (M1/M2/M3/M4) のみ対応。Windows/Linuxでは利用できません。メモリ32GB以上を推奨します。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"font-medium text-foreground",children:"必要なもの(事前に手動でインストール)"}),(0,t.jsx)("ul",{className:"list-disc pl-5 text-muted-foreground",children:(0,t.jsx)("li",{children:"Python 3.9以降(Xcode Command Line Tools または Homebrew経由で入手可能)"})})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"セットアップ・起動"}),(0,t.jsx)("p",{className:"text-muted-foreground",children:"下のボタンから、依存関係のインストール・モデルのダウンロード・サーバー起動まで自動で行われます(初回はモデルサイズが大きく数分〜数十分かかります)。"}),C&&!V&&(0,t.jsx)("p",{className:"mt-1 text-destructive",children:"llm-serverが見つかりませんでした。インストールが壊れている可能性があります。"})]}),(0,t.jsxs)("div",{children:[(0,t.jsx)("p",{className:"mb-1 font-medium text-foreground",children:"アンインストール"}),(0,t.jsxs)("p",{className:"text-muted-foreground",children:["ローカルLLMのためにダウンロードされるもの(Pythonの依存関係・LLMモデル、合計約17GB)は全て",(0,t.jsx)("code",{className:"rounded bg-muted px-1",children:"~/.tsunagi/llm"}),"に保存されます。不要になった場合はこのディレクトリを削除するだけで、関連リソースを完全に削除できます。"]})]}),(0,t.jsxs)("div",{className:"border-t border-border pt-4",children:[(!C||"not_running"===C.step||"error"===C.step)&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)(s.Button,{size:"default",onClick:()=>void O(),disabled:!C,children:[C?(0,t.jsx)(u.Bot,{}):(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"LLMサーバーを起動"]}),C?.step==="error"&&C.error&&(0,t.jsx)("p",{className:"text-xs/relaxed text-destructive",children:C.error})]}),C?.step==="installing_deps"&&(0,t.jsxs)(s.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"依存関係をインストール中... (数分かかる場合があります)"]}),C?.step==="downloading_model"&&(0,t.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,t.jsxs)("div",{className:"flex items-center gap-2 text-xs/relaxed text-muted-foreground",children:[(0,t.jsx)(a.Loader2,{className:"size-4 animate-spin"}),"モデルをダウンロード中... (約17GB)"]}),_&&(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(v.Progress,{value:z}),(0,t.jsxs)("p",{className:"text-xs/relaxed text-muted-foreground",children:[M(_.downloadedBytes)," / ",M(_.totalBytes)," (",z.toFixed(0),"%) ・ ",function(e){if(null===e)return"残り時間を計算中...";if(e<60)return`残り約${e}秒`;let t=Math.floor(e/60);return`残り約${t}分${e%60}秒`}(_.etaSeconds)]})]})]}),C?.step==="starting_server"&&(0,t.jsxs)(s.Button,{size:"default",disabled:!0,children:[(0,t.jsx)(a.Loader2,{className:"animate-spin"}),"サーバーを起動中... (モデルのロードに数十秒かかる場合があります)"]}),C&&j.includes(C.step)&&(0,t.jsxs)("div",{className:"flex flex-wrap items-center gap-2",children:[(0,t.jsxs)("span",{className:"flex items-center gap-2 text-success",children:[(0,t.jsx)(c.CheckCircle2,{className:"size-4"}),"サーバーは起動しています"]}),(0,t.jsxs)(s.Button,{size:"default",variant:"outline",onClick:()=>void B(),children:[(0,t.jsx)(n.Square,{}),"サーバーを停止"]})]})]})]})})]})}let C="tsunagi:whisper-prompt",k="tsunagi:llm-system-prompt",N=["running","running_external"];function S({onTranscribed:e}){let[d,u]=(0,r.useState)(!1),[c,f]=(0,r.useState)("idle"),[m,p]=(0,r.useState)(0),[v,g]=(0,r.useState)(null),x=(0,r.useRef)(null),b=(0,r.useRef)([]),y=(0,r.useRef)(null),j=(0,r.useRef)(null),M=(0,r.useRef)(null),w=(0,r.useRef)(0),E=(0,r.useRef)(0);(0,r.useEffect)(()=>{u("true"===localStorage.getItem("tsunagi:voice-input-enabled"))},[]),(0,r.useEffect)(()=>{if(!d)return;let e=!1,t=async()=>{try{let t=await fetch((0,l.apiUrl)("/api/whisper/server/status")),r=await t.json();e||g(r.step)}catch{e||g("not_running")}};t();let r=setInterval(t,5e3);return()=>{e=!0,clearInterval(r)}},[d]);let L=(0,r.useCallback)(()=>{M.current&&(clearInterval(M.current),M.current=null),j.current?.close(),j.current=null,p(0)},[]);(0,r.useEffect)(()=>()=>{L()},[L]);let R=(0,r.useCallback)(e=>{let t=new AudioContext,r=t.createMediaStreamSource(e),a=t.createAnalyser();a.fftSize=2048,r.connect(a),j.current=t,w.current=0,E.current=0;let i=new Float32Array(a.fftSize);M.current=setInterval(()=>{a.getFloatTimeDomainData(i);let e=0;for(let t=0;t<i.length;t++)e+=i[t]*i[t];let t=Math.sqrt(e/i.length),r=Math.min(1,Math.max(0,((t>0?20*Math.log10(t):-60)- -60)/50));E.current=Math.max(E.current,r),w.current=.3*w.current+.7*r,p(w.current)},50)},[]),I=(0,r.useCallback)(async t=>{f("transcribing");try{let r=new FormData,a=localStorage.getItem(C);a&&r.append("prompt",a);let i="true"===localStorage.getItem(h);r.append("useLlm",String(i));let n=localStorage.getItem(k);n&&r.append("systemPrompt",n),r.append("file",t,"recording.webm");let s=await fetch((0,l.apiUrl)("/api/whisper/transcribe"),{method:"POST",body:r});if(!s.ok){let e=await s.json().catch(()=>null);throw Error(e?.error||`HTTPエラー: ${s.status}`)}let d=await s.json();d.warning&&o.toaster.create({type:"error",title:"LLM整形をスキップしました",description:d.warning}),d.text&&e(d.text)}catch(e){o.toaster.create({type:"error",title:"文字起こしに失敗しました",description:e instanceof Error?e.message:String(e)})}finally{f("idle")}},[e]),T=(0,r.useCallback)(async()=>{try{let e=await navigator.mediaDevices.getUserMedia({audio:!0});y.current=e,b.current=[],R(e);let t=new MediaRecorder(e,{audioBitsPerSecond:128e3});t.ondataavailable=e=>{e.data.size>0&&b.current.push(e.data)},t.onstop=()=>{y.current?.getTracks().forEach(e=>e.stop()),y.current=null;let e=E.current;if(L(),e<.15){o.toaster.create({type:"error",title:"音声が検出されませんでした",description:"マイクに向かって話してから、もう一度お試しください。"}),f("idle");return}let t=new Blob(b.current,{type:"audio/webm"});I(t)},x.current=t,t.start(),f("recording")}catch(e){o.toaster.create({type:"error",title:"マイクにアクセスできませんでした",description:e instanceof Error?e.message:String(e)})}},[I,R,L]),P=(0,r.useCallback)(()=>{x.current?.stop()},[]);if(!d)return null;let O=null!==v&&N.includes(v);return(0,t.jsxs)("div",{className:"recording"===c?"inline-flex items-center gap-2 rounded-md bg-destructive/10 p-1":"inline-flex",children:[(0,t.jsx)(s.Button,{size:"icon",variant:"recording"===c?"destructive":"default",onClick:"recording"===c?P:()=>void T(),disabled:"transcribing"===c||"idle"===c&&!O,title:"recording"===c?"停止して入力":"idle"!==c||O?"音声入力":"Whisperサーバーが起動していません(Settingsから起動してください)",children:"transcribing"===c?(0,t.jsx)(a.Loader2,{className:"w-4 h-4 animate-spin"}):"recording"===c?(0,t.jsx)(n.Square,{className:"w-4 h-4"}):(0,t.jsx)(i.Mic,{className:"w-4 h-4"})}),"recording"===c&&(0,t.jsx)("div",{"aria-hidden":!0,className:"h-1.5 w-16 overflow-hidden rounded-full bg-background/60",children:(0,t.jsx)("div",{className:"h-full rounded-full bg-destructive",style:{width:`${Math.round(100*m)}%`}})})]})}},51846,e=>{"use strict";var t=e.i(71645);e.s(["useDocumentTitle",0,function(e){(0,t.useEffect)(()=>{let t=e?`繋 | ${e}`:"繋",r=document.title;return document.title=t,()=>{document.title=r}},[e])}])}]);
@@ -19,6 +19,14 @@ const HF_CACHE_DIR = path.join(TSUNAGI_LLM_DIR, 'cache');
19
19
  const MODEL_CACHE_DIR = path.join(HF_CACHE_DIR, 'hub', 'models--mlx-community--Qwen3-30B-A3B-Instruct-2507-4bit');
20
20
  // 実測値(2026-07時点、4bit量子化された重み一式の合計)。多少の変動はあるがETA計算の目安として使う。
21
21
  const EXPECTED_MODEL_BYTES = 17_200_000_000;
22
+ // starting_serverフェーズ(spawn後にhealthyになるまで待つ時間)の上限。
23
+ // mlx_lm.serverは/healthをモデル本体のロード完了を待たずに返す(ロードはバックグラウンド
24
+ // スレッドで進む)ため、この待ち時間の大半はPython起動/import初期化のばらつきに対する
25
+ // マージン。whisperより依存関係が重いぶん長めの90秒を維持する。
26
+ // プロセスが起動途中でクラッシュした場合はこのタイムアウトを待たず即座に検知する。
27
+ const STARTUP_TIMEOUT_MS = 90_000;
28
+ // クラッシュ時にエラーメッセージへ含めるstderrの上限文字数。
29
+ const STDERR_TAIL_MAX_CHARS = 4000;
22
30
  // このファイルは apps/server/src/lib (dev) または apps/cli/dist/server/lib
23
31
  // (npm配布物) のいずれかにいる。どちらの場合も3階層上に llm-server が
24
32
  // 兄弟ディレクトリとして存在するようレイアウトを揃えている
@@ -150,25 +158,34 @@ async function runSetupAndStart(dir) {
150
158
  downloadProgress = undefined;
151
159
  }
152
160
  currentStep = 'starting_server';
153
- const child = spawn(venvPython(), ['-m', 'mlx_lm.server', '--model', MODEL, '--host', '127.0.0.1', '--port', String(LLM_PORT)], { cwd: dir, stdio: 'ignore', env: hfEnv });
154
- child.on('exit', () => {
161
+ const child = spawn(venvPython(), ['-m', 'mlx_lm.server', '--model', MODEL, '--host', '127.0.0.1', '--port', String(LLM_PORT)], { cwd: dir, stdio: ['ignore', 'ignore', 'pipe'], env: hfEnv });
162
+ let stderrTail = '';
163
+ child.stderr?.on('data', (chunk) => {
164
+ stderrTail = (stderrTail + chunk.toString()).slice(-STDERR_TAIL_MAX_CHARS);
165
+ });
166
+ // healthポーリングとは別に、プロセスが起動途中で終了したことをタイムアウトを待たず検知する。
167
+ let exitInfo = null;
168
+ child.on('exit', (code, signal) => {
169
+ exitInfo = `exit code=${code} signal=${signal}`;
155
170
  if (managedProcess === child)
156
171
  managedProcess = null;
157
172
  });
158
- child.on('error', () => {
173
+ child.on('error', (err) => {
174
+ exitInfo = `spawn error: ${err.message}`;
159
175
  if (managedProcess === child)
160
176
  managedProcess = null;
161
177
  });
162
178
  managedProcess = child;
163
- // MoEで実計算はアクティブパラメータ(~3B)分のみだが、4bit量子化でも~17GBの
164
- // 重みファイル自体はメモリへ展開する必要があるため、whisperより長めに待つ。
165
179
  const start = Date.now();
166
- while (Date.now() - start < 90000) {
180
+ while (Date.now() - start < STARTUP_TIMEOUT_MS) {
181
+ if (exitInfo) {
182
+ throw new Error(`llm-server crashed while starting (${exitInfo})${stderrTail ? `\n${stderrTail}` : ''}`);
183
+ }
167
184
  if (await checkHealth())
168
185
  return;
169
186
  await sleep(1000);
170
187
  }
171
- throw new Error('Server did not become healthy within 90s of starting');
188
+ throw new Error(`Server did not become healthy within ${STARTUP_TIMEOUT_MS / 1000}s of starting`);
172
189
  }
173
190
  export function startLlmServer() {
174
191
  if (setupPromise || managedProcess) {
@@ -0,0 +1,9 @@
1
+ export interface VoiceDebugLogEntry {
2
+ whisperPrompt: string | undefined;
3
+ whisperText: string;
4
+ useLlm: boolean;
5
+ llmSystemPrompt?: string;
6
+ correctedText?: string;
7
+ llmError?: string;
8
+ }
9
+ export declare function appendVoiceDebugLog(entry: VoiceDebugLogEntry): void;
@@ -0,0 +1,28 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ // apps/server直下(src/libとdist/libのどちらから見ても2階層上)に置く。
5
+ // docker-compose(compose.yml)ではリポジトリ全体がbind mountされているため、
6
+ // ホスト側からもこのファイルをそのまま読める(named volumeの~/.tsunagiとは違い、
7
+ // Claude Codeが次回セッションでRead/grepしてプロンプト改善の材料にできる)。
8
+ const LOG_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', '..', 'logs');
9
+ const LOG_FILE = path.join(LOG_DIR, 'voice-input-debug.log');
10
+ // デバッグ用途のため肥大化を防ぐ簡易ローテーション(直近件数のみ保持)。
11
+ const MAX_ENTRIES = 200;
12
+ // Whisper生テキストとLLM整形結果を1リクエスト1行(JSON Lines)で追記する。
13
+ // プロンプト改善のデバッグ専用ログであり、書き込み失敗はリクエスト自体を
14
+ // 失敗させるべきではないため例外を握りつぶす。
15
+ export function appendVoiceDebugLog(entry) {
16
+ try {
17
+ fs.mkdirSync(LOG_DIR, { recursive: true });
18
+ const line = JSON.stringify({ timestamp: new Date().toISOString(), ...entry });
19
+ fs.appendFileSync(LOG_FILE, `${line}\n`);
20
+ const lines = fs.readFileSync(LOG_FILE, 'utf-8').split('\n').filter(Boolean);
21
+ if (lines.length > MAX_ENTRIES) {
22
+ fs.writeFileSync(LOG_FILE, `${lines.slice(-MAX_ENTRIES).join('\n')}\n`);
23
+ }
24
+ }
25
+ catch {
26
+ // デバッグログの書き込み失敗は無視する(本処理に影響させない)。
27
+ }
28
+ }
@@ -15,6 +15,12 @@ const HF_CACHE_DIR = path.join(TSUNAGI_WHISPER_DIR, 'cache');
15
15
  const MODEL_CACHE_DIR = path.join(HF_CACHE_DIR, 'hub', 'models--mlx-community--whisper-large-v3-turbo');
16
16
  // 実測値(2026-07時点、encoder+decoder等の合計)。多少の変動はあるがETA計算の目安として使う。
17
17
  const EXPECTED_MODEL_BYTES = 1_614_000_000;
18
+ // starting_serverフェーズ(spawn後にhealthyになるまで待つ時間)の上限。
19
+ // 遅いマシンでのPython起動/import/Metal初期化のばらつきを吸収するため60秒に設定。
20
+ // プロセスが起動途中でクラッシュした場合はこのタイムアウトを待たず即座に検知する。
21
+ const STARTUP_TIMEOUT_MS = 60_000;
22
+ // クラッシュ時にエラーメッセージへ含めるstderrの上限文字数。
23
+ const STDERR_TAIL_MAX_CHARS = 4000;
18
24
  // このファイルは apps/server/src/lib (dev) または apps/cli/dist/server/lib
19
25
  // (npm配布物) のいずれかにいる。どちらの場合も3階層上に whisper-server が
20
26
  // 兄弟ディレクトリとして存在するようレイアウトを揃えている
@@ -146,23 +152,34 @@ async function runSetupAndStart(dir) {
146
152
  downloadProgress = undefined;
147
153
  }
148
154
  currentStep = 'starting_server';
149
- const child = spawn(venvPython(), ['-m', 'uvicorn', 'server:app', '--host', '127.0.0.1', '--port', '8765'], { cwd: dir, stdio: 'ignore', env: hfEnv });
150
- child.on('exit', () => {
155
+ const child = spawn(venvPython(), ['-m', 'uvicorn', 'server:app', '--host', '127.0.0.1', '--port', '8765'], { cwd: dir, stdio: ['ignore', 'ignore', 'pipe'], env: hfEnv });
156
+ let stderrTail = '';
157
+ child.stderr?.on('data', (chunk) => {
158
+ stderrTail = (stderrTail + chunk.toString()).slice(-STDERR_TAIL_MAX_CHARS);
159
+ });
160
+ // healthポーリングとは別に、プロセスが起動途中で終了したことをタイムアウトを待たず検知する。
161
+ let exitInfo = null;
162
+ child.on('exit', (code, signal) => {
163
+ exitInfo = `exit code=${code} signal=${signal}`;
151
164
  if (managedProcess === child)
152
165
  managedProcess = null;
153
166
  });
154
- child.on('error', () => {
167
+ child.on('error', (err) => {
168
+ exitInfo = `spawn error: ${err.message}`;
155
169
  if (managedProcess === child)
156
170
  managedProcess = null;
157
171
  });
158
172
  managedProcess = child;
159
173
  const start = Date.now();
160
- while (Date.now() - start < 30000) {
174
+ while (Date.now() - start < STARTUP_TIMEOUT_MS) {
175
+ if (exitInfo) {
176
+ throw new Error(`whisper-server crashed while starting (${exitInfo})${stderrTail ? `\n${stderrTail}` : ''}`);
177
+ }
161
178
  if (await checkHealth())
162
179
  return;
163
180
  await sleep(1000);
164
181
  }
165
- throw new Error('Server did not become healthy within 30s of starting');
182
+ throw new Error(`Server did not become healthy within ${STARTUP_TIMEOUT_MS / 1000}s of starting`);
166
183
  }
167
184
  export function startWhisperServer() {
168
185
  if (setupPromise || managedProcess) {
@@ -1,12 +1,15 @@
1
1
  import { getWhisperServerStatus, startWhisperServer, stopWhisperServer, } from '../lib/whisper-process.js';
2
2
  import { generateLlmCompletion } from '../lib/llm-process.js';
3
+ import { appendVoiceDebugLog } from '../lib/voice-debug-log.js';
3
4
  // ユーザーが依存関係(pipパッケージ)をセットアップ済みのローカル常駐サーバー
4
5
  // (apps/whisper-server)を指す。依存関係のインストール自体はtsunagi側では行わず、
5
6
  // プロセスの起動・停止のみ管理する。
6
7
  const WHISPER_SERVER_URL = process.env.TSUNAGI_WHISPER_SERVER_URL || 'http://127.0.0.1:8765';
7
- // 文字起こし結果のLLM整形に使うシステムプロンプト。検証で
8
- // 「原文の言葉遣いを維持しつつ句読点・誤字だけ直す」設定が有効と確認できたもの。
9
- const TRANSCRIPTION_CORRECTION_SYSTEM_PROMPT = '以下は音声認識による文字起こし結果です。元の言葉遣いや言い回し、文章構成はできるだけそのまま維持してください。行ってよいのは句読点の追加と、明らかな誤字(音声認識の誤変換)の修正のみです。要約・言い換え・フィラーの除去・文の並び替えなどは行わないでください。修正後の文章のみを出力し、前置きや説明は不要です。';
8
+ // 文字起こし結果のLLM整形に使うシステムプロンプト(既定値)。Settings画面で
9
+ // 上書きされなかった場合に使う。同音異義語の誤変換(例:「句読点」→「句頭点」、
10
+ // 「誤字」→「語字」)を放置してしまう問題があったため、該当パターンを明示して
11
+ // 一語ずつ文脈と照合するよう指示している。
12
+ const DEFAULT_TRANSCRIPTION_CORRECTION_SYSTEM_PROMPT = '以下は音声認識(Whisper)による文字起こし結果です。元の言葉遣いや言い回し、文章構成はできるだけそのまま維持してください。行ってよいのは次の2つだけです。(1)句読点の追加。(2)音声認識特有の誤字の修正。日本語の音声認識では、発音が同じで意味の異なる漢字に変換されることがよくあります(例:「句読点」→「句頭点」、「誤字」→「語字」など)。一見すると自然な単語に見えても文脈に合わない場合は誤変換である可能性が高いため、一語ずつ文脈に合っているか確認し、明らかな誤りは正しい漢字に修正してください。要約・言い換え・フィラーの除去・文の並び替えなど、上記2つ以外の変更は行わないでください。修正後の文章のみを出力し、前置きや説明は不要です。';
10
13
  export async function whisperRoutes(fastify) {
11
14
  // GET /whisper/server/status
12
15
  fastify.get('/whisper/server/status', async (_request, reply) => {
@@ -41,6 +44,12 @@ export async function whisperRoutes(fastify) {
41
44
  // ローカルLLMによる整形を使うかどうかはクライアント(Settingsの有効/無効フラグ)から渡される。
42
45
  const useLlmField = file.fields.useLlm;
43
46
  const useLlm = useLlmField?.value === 'true';
47
+ // LLM整形用システムプロンプトもSettings画面で編集・保存でき、指定があれば
48
+ // 既定のプロンプトの代わりに使う(空文字列の場合は既定のプロンプトを使う)。
49
+ const systemPromptField = file.fields.systemPrompt;
50
+ const systemPrompt = typeof systemPromptField?.value === 'string' && systemPromptField.value.trim()
51
+ ? systemPromptField.value
52
+ : DEFAULT_TRANSCRIPTION_CORRECTION_SYSTEM_PROMPT;
44
53
  const buffer = await file.toBuffer();
45
54
  const body = new FormData();
46
55
  body.append('file', new Blob([buffer]), file.filename);
@@ -67,6 +76,7 @@ export async function whisperRoutes(fastify) {
67
76
  });
68
77
  }
69
78
  if (!useLlm || !whisperText) {
79
+ appendVoiceDebugLog({ whisperPrompt: prompt, whisperText, useLlm });
70
80
  return reply.status(200).send({ text: whisperText });
71
81
  }
72
82
  // LLMによる整形が有効な場合のみ追加で問い合わせる。文字起こし自体は既に
@@ -74,13 +84,28 @@ export async function whisperRoutes(fastify) {
74
84
  // (ユーザーの発話内容を無駄にしないことを、整形の成否より優先する)。
75
85
  try {
76
86
  const corrected = await generateLlmCompletion([
77
- { role: 'system', content: TRANSCRIPTION_CORRECTION_SYSTEM_PROMPT },
87
+ { role: 'system', content: systemPrompt },
78
88
  { role: 'user', content: whisperText },
79
89
  ], Math.min(4096, Math.max(512, whisperText.length * 4)));
80
- return reply.status(200).send({ text: corrected.trim() });
90
+ const correctedText = corrected.trim();
91
+ appendVoiceDebugLog({
92
+ whisperPrompt: prompt,
93
+ whisperText,
94
+ useLlm,
95
+ llmSystemPrompt: systemPrompt,
96
+ correctedText,
97
+ });
98
+ return reply.status(200).send({ text: correctedText });
81
99
  }
82
100
  catch (error) {
83
101
  fastify.log.error(error, 'LLM correction failed, falling back to raw transcription');
102
+ appendVoiceDebugLog({
103
+ whisperPrompt: prompt,
104
+ whisperText,
105
+ useLlm,
106
+ llmSystemPrompt: systemPrompt,
107
+ llmError: error instanceof Error ? error.message : String(error),
108
+ });
84
109
  return reply.status(200).send({
85
110
  text: whisperText,
86
111
  warning: 'ローカルLLMによる整形に失敗したため、文字起こし結果をそのまま使用しました',