ijihun-planner-studio 0.1.1 → 0.1.3
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.
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{color:#1f1f23;background:#eef2f7;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Apple SD Gothic Neo,Noto Sans KR,Segoe UI,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;--ink: #1f1f23;--muted: #72737a;--line: #d7dbe3;--heavy-line: #25252a;--paper: #ffffff;--panel: #f8fafc;--panel-strong: #f1f4f9;--cobalt: #315ddc;--green: #26886e;--yellow: #f4c84f;--danger: #be3a3a;--shadow-sm: 0 8px 22px rgba(31, 35, 42, .07);--shadow-md: 0 18px 48px rgba(31, 35, 42, .12);--focus: rgba(49, 93, 220, .24)}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh;background:linear-gradient(90deg,rgba(31,31,35,.035) 1px,transparent 1px),linear-gradient(rgba(31,31,35,.035) 1px,transparent 1px),var(--panel-strong);background-size:34px 34px}::selection{background:#315ddc2e}button,input,select,textarea{font:inherit}button{border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink);cursor:pointer;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:0 12px;font-size:13px;font-weight:750;transition:border-color .16s ease,background-color .16s ease,color .16s ease,box-shadow .16s ease,transform .16s ease}button:hover:not(:disabled){border-color:#b8c0cc;background:#f7f9fd;box-shadow:0 5px 14px #1f232a14}button:active:not(:disabled){transform:translateY(1px)}button:disabled{cursor:not-allowed;opacity:.56}input,select,textarea{border:1px solid var(--line);border-radius:8px;color:var(--ink);background:#fff;min-height:34px;transition:border-color .16s ease,box-shadow .16s ease,background-color .16s ease}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid var(--focus);outline-offset:2px}input::placeholder,textarea::placeholder{color:#a2a8b3}textarea{resize:vertical}.login-shell{min-height:100vh;display:grid;place-items:center;padding:clamp(20px,5vw,48px)}.login-card{width:min(430px,100%);background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow-md);padding:30px;position:relative;overflow:hidden}.login-card:before{content:"";position:absolute;inset:0 0 auto;height:4px;background:linear-gradient(90deg,var(--cobalt),var(--green),var(--yellow))}.login-mark{width:46px;height:46px;border:1px solid #c8d3ef;border-radius:8px;display:grid;place-items:center;margin-bottom:18px;background:#f5f7fc;color:var(--cobalt)}.login-card h1{margin:4px 0 0;font-size:30px;line-height:1.08}.login-card p{margin:12px 0 20px;color:var(--muted);line-height:1.5}.login-error{border:1px solid #f0b8b8;background:#fff6f6;color:#9c2f2f!important;border-radius:8px;padding:10px 12px;font-size:13px}.login-form{display:grid;gap:12px}.login-form label{display:grid;gap:6px;font-size:13px;font-weight:800;color:#4f5159}.login-form input{min-height:42px;padding:0 12px}.login-form button{min-height:42px;background:var(--ink);color:#fff;border-color:var(--ink);box-shadow:0 10px 22px #1f1f2329}.login-form button:hover:not(:disabled){background:#111215;border-color:#111215;color:#fff}.app-shell{display:grid;grid-template-columns:248px minmax(700px,1fr) 348px;gap:16px;min-height:100vh;padding:16px}.status-rail,.reminder-panel,.block-editor,.mandarart-actions-panel{background:var(--panel);border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow-sm)}.status-rail{display:flex;flex-direction:column;gap:14px;padding:14px;position:sticky;top:14px;height:calc(100vh - 28px);overflow:auto}.brand-block{display:grid;grid-template-columns:42px 1fr;gap:10px;align-items:center}.brand-mark{width:42px;height:42px;border:1px solid #c8d3ef;border-radius:8px;display:grid;place-items:center;font-weight:900;background:#f5f7fc;color:var(--cobalt)}.brand-block strong,.brand-block span{display:block}.brand-block strong{font-size:15px}.brand-block span,.rail-note,.sync-panel dd,.sync-panel dt,.section-kicker{color:var(--muted);font-size:12px;font-weight:850;text-transform:uppercase;letter-spacing:0}.date-card{display:grid;gap:8px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px;box-shadow:0 8px 18px #1f232a0d}.date-card>span,.sync-heading,.panel-title-row{display:flex;align-items:center;gap:8px}.date-card input{width:100%;padding:0 10px;background:#fbfcff}.date-card strong{font-size:22px}.mode-switch{display:grid;gap:8px}.mode-switch button{justify-content:flex-start;min-height:42px;background:#fff}.mode-switch button.active{border-color:var(--ink);background:var(--ink);color:#fff;box-shadow:inset 0 0 0 1px #ffffff14}.sync-panel{background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px;box-shadow:0 8px 18px #1f232a0d}.sync-heading{justify-content:space-between;margin-bottom:10px}.sync-heading strong{margin-right:auto}.sync-panel dl{display:grid;gap:9px;margin:0}.sync-panel dl>div{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.sync-panel dd{margin:0;color:var(--ink);text-align:right}.rail-actions{display:grid;gap:8px}.rail-actions button,.file-button{justify-content:flex-start;min-height:38px;background:#fff}.file-button{border:1px solid var(--line);border-radius:8px;background:#fff;padding:0 12px;display:flex;align-items:center;gap:7px;cursor:pointer;font-size:13px;font-weight:750}.file-button input{display:none}.rail-note{margin-top:auto;display:flex;gap:8px;line-height:1.45}.main-column{min-width:0;display:grid;grid-template-rows:auto minmax(0,1fr);gap:14px}.topbar{min-height:68px;display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px 14px;box-shadow:var(--shadow-sm);position:sticky;top:16px;z-index:4}.topbar h2,.panel-title-row h2{margin:1px 0 0;font-size:20px;line-height:1.15}.topbar-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.subtle-button{background:#f7f9fd;color:#4b5361}.status-pill{border:1px solid var(--line);border-radius:8px;background:#f5f7fc;padding:8px 10px;font-size:12px;font-weight:800;color:#4b5361}.workspace{min-width:0;min-height:0}.timebox-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}.planner-sheet{background:var(--paper);border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow-md)}.timebox-sheet{padding:28px;min-height:calc(100vh - 108px);background:linear-gradient(#fff,#fff),linear-gradient(90deg,#315ddc0d,#26886e0a)}.sheet-header{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;margin-bottom:24px}.sheet-header h1{margin:0;font-size:clamp(34px,4vw,66px);line-height:.96;font-weight:950;text-wrap:balance}.sheet-header span,.date-line span{color:var(--muted)}.date-line{display:flex;align-items:baseline;gap:12px;border-bottom:2px solid var(--ink);min-width:240px;padding-bottom:8px;font-size:18px}.date-line strong{font-size:24px;font-weight:900}.timebox-grid-layout{display:grid;grid-template-columns:minmax(260px,.93fr) minmax(360px,1fr);grid-template-rows:auto auto 1fr auto;gap:18px 24px;grid-template-areas:"top gratitude" "brain time" "brain time" "brain memo"}.paper-section{min-width:0}.paper-section h2{margin:0 0 12px;font-size:18px;font-weight:900;border-bottom:2px solid var(--ink);padding-bottom:10px;display:flex;align-items:center;gap:8px}.top-three{grid-area:top}.gratitude{grid-area:gratitude}.brain-dump{grid-area:brain}.time-plan{grid-area:time}.memo{grid-area:memo}.top-input{display:grid;grid-template-columns:24px minmax(0,1fr) 36px;gap:8px;align-items:center;border-bottom:1px solid var(--line);min-height:46px}.top-input span{font-weight:900;color:var(--cobalt)}.top-input input,.gratitude input{border:0;border-radius:0;min-width:0}.top-input input:focus,.gratitude input:focus,.brain-dump textarea:focus,.memo textarea:focus,.time-block input:focus,.mandarart-cell:focus{outline:2px solid var(--focus);outline-offset:-2px}.top-input button{width:32px;min-height:30px;padding:0}.brain-dump textarea{width:100%;min-height:720px;border:0;border-radius:0;line-height:30px;background-image:linear-gradient(#eef1f5 1px,transparent 1px),linear-gradient(90deg,#f4f6f8 1px,transparent 1px);background-size:32px 30px;padding:10px 8px}.gratitude input{width:100%;border-bottom:1px solid var(--line)}.section-toolbar{display:flex;justify-content:space-between;gap:10px;align-items:flex-start}.section-toolbar h2{flex:1}.timeline-wrap{position:relative}.minute-labels{display:grid;grid-template-columns:44px repeat(6,1fr);color:#b5bac5;font-size:11px;border-bottom:1px solid var(--line)}.minute-labels span{text-align:right;padding:0 4px 3px 0}.timeline-grid{position:relative;border-bottom:2px solid var(--ink);background:repeating-linear-gradient(to right,transparent 0,transparent calc((100% - 44px) / 6 - 1px),#e9edf2 calc((100% - 44px) / 6)),linear-gradient(to right,transparent 0 44px,#f8fafc 44px 100%)}.hour-row{display:grid;grid-template-columns:44px 1fr;height:46px;border-bottom:1px solid #d2d6de}.hour-row span{font-weight:800;padding-top:7px;color:var(--ink)}.time-block{position:absolute;left:50px;right:8px;border:1px solid rgba(49,93,220,.35);border-left:4px solid var(--cobalt);background:#315ddc14;border-radius:8px;display:grid;grid-template-columns:minmax(0,1fr) auto 24px;align-items:center;gap:8px;padding:5px 6px;overflow:hidden;box-shadow:0 5px 12px #315ddc14}.time-block.priority{border-color:#f4c84fb8;border-left-color:var(--yellow);background:#f4c84f29}.time-block input{min-width:0;min-height:24px;border:0;background:transparent;font-weight:800}.time-block span{color:var(--muted);font-size:12px;white-space:nowrap}.time-block button{width:22px;min-height:22px;padding:0;background:transparent}.memo textarea{min-height:142px;width:100%;border:0;border-radius:0;line-height:34px;background-image:linear-gradient(#d8dce3 1px,transparent 1px);background-size:100% 34px;padding:6px 0}.block-editor{padding:14px;overflow:auto}.panel-title-row{justify-content:space-between;margin-bottom:14px}.block-list,.action-list,.reminder-list{display:grid;gap:8px}.block-row{display:grid;grid-template-columns:minmax(140px,1fr) 88px 88px 74px 34px 34px;gap:6px;align-items:center;background:#fff;border:1px solid var(--line);border-radius:8px;padding:8px;transition:border-color .16s ease,box-shadow .16s ease,transform .16s ease}.block-row:hover,.reminder-row:hover,.action-row:hover{border-color:#bdc5d2;box-shadow:0 8px 18px #1f232a12}.block-row input{min-width:0;padding:0 8px}.block-row label{display:flex;align-items:center;gap:4px;font-size:12px;color:var(--muted)}.block-row button{width:32px;padding:0}.reminder-panel{position:sticky;top:14px;height:calc(100vh - 28px);padding:14px;display:flex;flex-direction:column;min-width:0}.select-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;margin-bottom:10px}.select-row label{display:grid;gap:4px;color:var(--muted);font-size:12px}.select-row select{width:100%;padding:0 8px}.metric-strip{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px}.metric-strip div{background:#fff;border:1px solid var(--line);border-radius:8px;padding:10px;box-shadow:0 6px 14px #1f232a0d}.metric-strip strong,.metric-strip span{display:block}.metric-strip strong{font-size:22px;line-height:1}.metric-strip span{color:var(--muted);font-size:12px;margin-top:3px}.quick-create{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;margin-bottom:10px}.quick-create input{min-width:0;padding:0 10px;background:#fbfcff}.error-text{margin:0 0 10px;color:var(--danger);font-size:13px}.reminder-list{overflow:auto;padding-right:2px;flex:1}.reminder-row,.action-row{background:#fff;border:1px solid var(--line);border-radius:8px;padding:10px;display:grid;gap:9px;transition:border-color .16s ease,box-shadow .16s ease}.reminder-row strong,.reminder-row span{display:block}.reminder-row strong{line-height:1.25;overflow-wrap:anywhere}.reminder-row span{color:var(--muted);font-size:12px;margin-top:4px}.row-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.row-actions button,.action-row button{min-height:30px;padding:0}.google-sync-box{margin-top:12px;border-top:1px solid var(--line);padding-top:12px;display:grid;gap:8px}.google-sync-box span{color:var(--muted);font-size:12px;line-height:1.42}.google-sync-box div{display:grid;grid-template-columns:1fr 1fr;gap:8px}.empty-state{background:#fff;border:1px dashed #c9ced7;border-radius:8px;padding:18px;display:grid;gap:6px;color:var(--muted);text-align:center;box-shadow:inset 0 0 0 1px #ffffffa6}.empty-state strong{color:var(--ink)}.empty-state.compact{padding:14px;font-size:13px}.mandarart-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}.mandarart-sheet{padding:28px;background:#fff}.mandarart-grid{width:min(100%,860px);aspect-ratio:1;margin:0 auto;display:grid;grid-template-columns:repeat(9,minmax(0,1fr));grid-template-rows:repeat(9,minmax(0,1fr));border:2px solid var(--heavy-line);box-shadow:0 12px 26px #1f232a14}.mandarart-cell{width:100%;height:100%;min-height:0;border:0;border-right:1px solid var(--heavy-line);border-bottom:1px solid var(--heavy-line);border-radius:0;resize:none;padding:8px;text-align:center;display:block;font-size:clamp(11px,.95vw,14px);line-height:1.28;overflow:hidden}.mandarart-cell.theme-cell{background:#eef2f7;font-weight:850}.mandarart-cell.main-goal{background:var(--ink);color:#fff;font-weight:900}.mandarart-cell.thick-left{border-left:2px solid var(--heavy-line)}.mandarart-cell.thick-top{border-top:2px solid var(--heavy-line)}.mandarart-cell.thick-right{border-right:2px solid var(--heavy-line)}.mandarart-cell.thick-bottom{border-bottom:2px solid var(--heavy-line)}.mandarart-actions-panel{padding:14px;overflow:auto}.mandarart-actions-panel p{margin:0 0 14px;color:var(--muted);font-size:13px;line-height:1.5}.action-row{grid-template-columns:minmax(0,1fr) auto;align-items:center}.action-row span{overflow-wrap:anywhere;line-height:1.35}.action-row div{display:flex;gap:6px}.wide-action{width:100%;margin-top:12px;background:var(--ink);color:#fff;border-color:var(--ink)}.wide-action:hover:not(:disabled){background:#111215;border-color:#111215;color:#fff}.icon-button{width:34px;min-height:34px;padding:0}.toast{position:fixed;left:50%;bottom:18px;transform:translate(-50%);display:flex;align-items:center;gap:8px;max-width:min(560px,calc(100vw - 28px));background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:var(--shadow-md);padding:11px 14px;z-index:20;font-size:13px;font-weight:750}.toast.ok svg{color:var(--green)}.toast.error svg{color:var(--danger)}.spin{animation:spin .9s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1240px){.app-shell{grid-template-columns:220px minmax(620px,1fr)}.reminder-panel{grid-column:1 / -1;position:static;height:auto;max-height:520px}}@media(max-width:920px){.app-shell,.timebox-workspace,.mandarart-workspace,.timebox-grid-layout{grid-template-columns:1fr}.app-shell{padding:8px}.topbar{position:static;align-items:flex-start}.topbar-actions{width:100%;justify-content:flex-start}.status-rail{position:static;height:auto}.timebox-grid-layout{grid-template-areas:"top" "gratitude" "brain" "time" "memo"}.timebox-sheet,.mandarart-sheet{padding:18px}.brain-dump textarea{min-height:260px}.block-row{grid-template-columns:1fr 1fr}.mandarart-grid{width:100%}}@media(max-width:560px){.login-card{padding:24px}.login-card h1{font-size:27px}.app-shell{gap:10px}.topbar{display:grid}.status-pill,.topbar-actions button{min-height:32px}.sheet-header{display:grid}.date-line{min-width:0;width:100%}.row-actions{grid-template-columns:repeat(3,minmax(36px,1fr))}}@media print{body{background:#fff}.status-rail,.reminder-panel,.topbar,.block-editor,.mandarart-actions-panel,.toast{display:none}.app-shell,.main-column,.workspace,.timebox-workspace,.mandarart-workspace{display:block;padding:0}.planner-sheet{border:0;box-shadow:none;min-height:auto}}
|
|
@@ -174,4 +174,4 @@ Error generating stack: `+i.message+`
|
|
|
174
174
|
* See the LICENSE file in the root directory of this source tree.
|
|
175
175
|
*/const Rd=ve("Wifi",[["path",{d:"M12 20h.01",key:"zekei9"}],["path",{d:"M2 8.82a15 15 0 0 1 20 0",key:"dnpr2z"}],["path",{d:"M5 12.859a10 10 0 0 1 14 0",key:"1x1e6c"}],["path",{d:"M8.5 16.429a5 5 0 0 1 7 0",key:"1bycff"}]]);function Io(m=new Date){const y=m.getFullYear(),d=String(m.getMonth()+1).padStart(2,"0"),D=String(m.getDate()).padStart(2,"0");return`${y}-${d}-${D}`}function Md(m){const y=new Date(`${m}T00:00:00`);return new Intl.DateTimeFormat("ko-KR",{weekday:"long"}).format(y)}function Od(m){const y=new Date(`${m}T00:00:00`);return new Intl.DateTimeFormat("ko-KR",{month:"long",day:"numeric"}).format(y)}function Wa(m){const[y,d]=m.split(":").map(Number);return y*60+d}function Dd({view:m,setView:y,date:d,setDate:D,health:E,onRefresh:M,onExportJson:R,onImportJson:C}){const j=(E==null?void 0:E.remindersBridge)&&(E==null?void 0:E.googleTasksBridge),F=E==null?void 0:E.googleSyncStatus,H=(F==null?void 0:F.last_success_at)||(F==null?void 0:F.last_success)||(F==null?void 0:F.completed_at);return s.jsxs("aside",{className:"status-rail",children:[s.jsxs("div",{className:"brand-block",children:[s.jsx("div",{className:"brand-mark",children:"PS"}),s.jsxs("div",{children:[s.jsx("strong",{children:"Planner Studio"}),s.jsx("span",{children:"Timebox + Mandarart"})]})]}),s.jsxs("label",{className:"date-card",children:[s.jsxs("span",{children:[s.jsx(yd,{size:16})," 날짜"]}),s.jsx("input",{type:"date",value:d,onChange:W=>D(W.target.value)}),s.jsx("strong",{children:Od(d)}),s.jsx("small",{children:Md(d)})]}),s.jsxs("nav",{className:"mode-switch","aria-label":"Planner mode",children:[s.jsxs("button",{className:m==="timebox"?"active":"",onClick:()=>y("timebox"),children:[s.jsx(Td,{size:18}),s.jsx("span",{children:"타임박스"})]}),s.jsxs("button",{className:m==="mandarart"?"active":"",onClick:()=>y("mandarart"),children:[s.jsx(Za,{size:18}),s.jsx("span",{children:"만다라트"})]})]}),s.jsxs("section",{className:"sync-panel",children:[s.jsxs("div",{className:"sync-heading",children:[j?s.jsx(Rd,{size:18}):s.jsx(Ld,{size:18}),s.jsx("strong",{children:"연동 상태"}),s.jsx("button",{className:"icon-button",onClick:M,"aria-label":"상태 새로고침",children:s.jsx(ba,{size:16})})]}),s.jsxs("dl",{children:[s.jsxs("div",{children:[s.jsx("dt",{children:"Apple Reminders"}),s.jsx("dd",{children:E!=null&&E.remindersBridge?"연결 가능":"확인 필요"})]}),s.jsxs("div",{children:[s.jsx("dt",{children:"Google Tasks"}),s.jsx("dd",{children:E!=null&&E.googleTasksBridge?"브리지 있음":"브리지 없음"})]}),s.jsxs("div",{children:[s.jsx("dt",{children:"최근 동기화"}),s.jsx("dd",{children:typeof H=="string"?H.replace("T"," ").slice(0,16):"상태 없음"})]})]})]}),s.jsxs("div",{className:"rail-actions",children:[s.jsxs("button",{onClick:R,children:[s.jsx(xd,{size:16}),"JSON 내보내기"]}),s.jsxs("label",{className:"file-button",children:[s.jsx(zd,{size:16}),"JSON 가져오기",s.jsx("input",{type:"file",accept:"application/json",onChange:W=>{var ge;const K=(ge=W.target.files)==null?void 0:ge[0];K&&C(K),W.currentTarget.value=""}})]}),s.jsxs("button",{onClick:()=>window.print(),children:[s.jsx(jd,{size:16}),"인쇄/PDF"]})]}),s.jsxs("div",{className:"rail-note",children:[s.jsx(qa,{size:16}),s.jsx("span",{children:"Reminders에 만든 항목은 기존 1분 동기화 도구를 통해 Google Tasks에도 반영될 수 있습니다."})]})]})}function Id({lists:m,reminders:y,selectedList:d,setSelectedList:D,loading:E,error:M,onRefresh:R,onAddTop:C,onSchedule:j,onComplete:F,onCreateQuick:H,onRunGoogleSync:W}){const K=y.filter(X=>!X.is_completed),ge=K.filter(X=>X.due_date===new Date().toISOString().slice(0,10)).length;return s.jsxs("aside",{className:"reminder-panel",children:[s.jsxs("div",{className:"panel-title-row",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-kicker",children:"Apple Reminders"}),s.jsx("h2",{children:"할 일 인박스"})]}),s.jsx("button",{className:"icon-button",onClick:R,disabled:E,"aria-label":"Reminders 새로고침",children:s.jsx(ba,{size:17,className:E?"spin":""})})]}),s.jsxs("div",{className:"select-row",children:[s.jsxs("label",{children:["목록",s.jsxs("select",{value:d,onChange:X=>D(X.target.value),children:[s.jsx("option",{value:"",children:"전체"}),m.map(X=>s.jsx("option",{value:X.title,children:X.title},X.id))]})]}),s.jsxs("button",{onClick:R,disabled:E,children:[s.jsx(qa,{size:16}),"가져오기"]})]}),s.jsxs("div",{className:"metric-strip",children:[s.jsxs("div",{children:[s.jsx("strong",{children:K.length}),s.jsx("span",{children:"불러온 미완료"})]}),s.jsxs("div",{children:[s.jsx("strong",{children:ge}),s.jsx("span",{children:"오늘 마감"})]})]}),s.jsx(Fd,{onCreateQuick:H}),M?s.jsx("p",{className:"error-text",children:M}):null,s.jsxs("div",{className:"reminder-list",children:[K.length===0&&!E?s.jsxs("div",{className:"empty-state",children:[s.jsx(Nd,{size:22}),s.jsx("strong",{children:"가져온 할 일이 없습니다"}),s.jsx("span",{children:"목록을 선택하고 새로고침하거나 빠른 추가로 Reminders에 바로 저장하세요."})]}):null,K.map(X=>s.jsxs("article",{className:"reminder-row",children:[s.jsxs("div",{children:[s.jsx("strong",{children:X.title}),s.jsxs("span",{children:[X.list_title,X.due_date?` · ${X.due_date}`:" · 날짜 없음"]})]}),s.jsxs("div",{className:"row-actions",children:[s.jsx("button",{title:"TOP3로 보내기",onClick:()=>C(X),children:s.jsx($n,{size:15})}),s.jsx("button",{title:"Time Plan에 배치",onClick:()=>j(X),children:s.jsx(ec,{size:15})}),s.jsx("button",{title:"완료 처리",onClick:()=>F(X),children:s.jsx(Ja,{size:15})})]})]},X.stable_id||X.id))]}),s.jsxs("div",{className:"google-sync-box",children:[s.jsx("strong",{children:"Google Tasks 동기화"}),s.jsx("span",{children:"기존 로컬 브리지가 Apple Reminders 변경사항을 Google Tasks로 동기화합니다."}),s.jsxs("div",{children:[s.jsx("button",{onClick:()=>W(!0),children:"미리보기"}),s.jsx("button",{onClick:()=>W(!1),children:"지금 동기화"})]})]})]})}function Fd({onCreateQuick:m}){const y=d=>{d.preventDefault();const D=new FormData(d.currentTarget),E=String(D.get("title")||"").trim();E&&(m(E),d.currentTarget.reset())};return s.jsxs("form",{className:"quick-create",onSubmit:y,children:[s.jsx("input",{name:"title",placeholder:"새 Reminders 할 일"}),s.jsxs("button",{type:"submit",children:[s.jsx($n,{size:16})," 추가"]})]})}const nc="planner-studio-v1",$o=[{core:30,block:10},{core:31,block:13},{core:32,block:16},{core:39,block:37},{core:41,block:43},{core:48,block:64},{core:49,block:67},{core:50,block:70}],Bo=40;function Fl(m){return{date:m,gratitude:"",topThree:["","",""],brainDump:"",memo:"",blocks:[]}}function rc(){return{cells:Array.from({length:81},()=>"")}}function Bn(m=Io()){return{version:1,days:{[m]:Fl(m)},mandarart:rc()}}function Ud(){var y,d;const m=localStorage.getItem(nc);if(!m)return Bn();try{const D=JSON.parse(m);return D.version!==1?Bn():{version:1,days:D.days||{},mandarart:((d=(y=D.mandarart)==null?void 0:y.cells)==null?void 0:d.length)===81?D.mandarart:rc()}}catch{return Bn()}}function Ad(m){localStorage.setItem(nc,JSON.stringify(m))}function lc(m){return`${m}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`}function $d(m,y=""){return{id:lc("block"),title:y,start:"09:00",end:"10:00",source:"manual"}}function Bd(m,y,d){const D=[...m];D[y]=d;const E=$o.find(M=>M.core===y||M.block===y);return E&&(D[E.core]=d,D[E.block]=d),D}function ic(m){const y=new Set([Bo,...$o.flatMap(d=>[d.core,d.block])]);return m.map((d,D)=>({title:d.trim(),index:D})).filter(d=>d.title&&!y.has(d.index))}const Vd=new Set([Bo,...$o.flatMap(m=>[m.core,m.block])]);function Hd({state:m,updateCell:y,onSendToTimebox:d,onCreateReminder:D}){const E=ic(m.cells);return s.jsxs("main",{className:"workspace mandarart-workspace",children:[s.jsxs("section",{className:"planner-sheet mandarart-sheet",children:[s.jsxs("div",{className:"sheet-header",children:[s.jsxs("div",{children:[s.jsx("h1",{children:"MANDAR-ART PLANNER"}),s.jsx("span",{children:"중심 목표 → 8개 주제 → 64개 실행 항목"})]}),s.jsx(Pd,{size:34})]}),s.jsx("div",{className:"mandarart-grid","aria-label":"Mandarart 9 by 9 grid",children:m.cells.map((M,R)=>{const C=Math.floor(R/9),j=R%9,F=R===Bo,H=Vd.has(R)&&!F;return s.jsx("textarea",{className:["mandarart-cell",F?"main-goal":"",H?"theme-cell":"",C%3===0?"thick-top":"",j%3===0?"thick-left":"",C===8?"thick-bottom":"",j===8?"thick-right":""].join(" "),value:M,placeholder:F?"중심 목표":H?"핵심 주제":"실행 항목",onChange:W=>y(R,W.target.value)},R)})})]}),s.jsxs("aside",{className:"mandarart-actions-panel",children:[s.jsxs("div",{className:"panel-title-row",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-kicker",children:"Action cells"}),s.jsx("h2",{children:"실행 항목"})]}),s.jsx(Za,{size:22})]}),s.jsx("p",{children:"중앙 목표와 8개 주제를 채운 뒤 세부 실행 항목을 오늘 타임박스나 Reminders로 보낼 수 있습니다."}),s.jsxs("div",{className:"action-list",children:[E.length===0?s.jsx("div",{className:"empty-state compact",children:"실행 항목 칸을 채워보세요."}):null,E.map(M=>s.jsxs("article",{className:"action-row",children:[s.jsx("span",{children:M.title}),s.jsxs("div",{children:[s.jsx("button",{title:"타임박스로 보내기",onClick:()=>d(M.title),children:s.jsx(ec,{size:15})}),s.jsx("button",{title:"Reminders 만들기",onClick:()=>D(M.title,"Mandarart 실행 항목"),children:s.jsx($n,{size:15})})]})]},M.index))]}),s.jsxs("button",{className:"wide-action",disabled:E.length===0,onClick:()=>E.slice(0,20).forEach(M=>D(M.title,"Mandarart 실행 항목")),children:[s.jsx(_d,{size:17}),"상위 20개 Reminders로 보내기"]})]})]})}const Qa=Array.from({length:20},(m,y)=>y+5),Ka=300,Fo=46;function Wd({plan:m,updatePlan:y,onCreateReminder:d}){const D=()=>{y(C=>({...C,blocks:[...C.blocks,$d(C.date)]}))},E=(C,j)=>{y(F=>({...F,blocks:F.blocks.map(H=>H.id===C?{...H,...j}:H)}))},M=C=>{y(j=>({...j,blocks:j.blocks.filter(F=>F.id!==C)}))},R=m.topThree.map(C=>C.trim()).filter(Boolean);return s.jsxs("main",{className:"workspace timebox-workspace",children:[s.jsxs("section",{className:"planner-sheet timebox-sheet",children:[s.jsxs("div",{className:"sheet-header",children:[s.jsxs("div",{children:[s.jsx("h1",{children:"TIME BOX PLANNER"}),s.jsx("span",{children:"by ultty · web edition"})]}),s.jsxs("div",{className:"date-line",children:[s.jsx("strong",{children:"date."}),s.jsx("span",{children:m.date.split("-").join(" / ")})]})]}),s.jsxs("div",{className:"timebox-grid-layout",children:[s.jsxs("section",{className:"paper-section top-three",children:[s.jsxs("h2",{children:[s.jsx(Sd,{size:18})," 오늘의 중요한 일 TOP3"]}),m.topThree.map((C,j)=>s.jsxs("div",{className:"top-input",children:[s.jsx("span",{children:j+1}),s.jsx("input",{value:C,placeholder:"가장 중요한 일",onChange:F=>{const H=[...m.topThree];H[j]=F.target.value,y(W=>({...W,topThree:H}))}}),s.jsx("button",{onClick:()=>C.trim()&&d(C.trim(),"Planner Studio TOP3"),children:s.jsx($n,{size:15})})]},j))]}),s.jsxs("section",{className:"paper-section gratitude",children:[s.jsx("h2",{children:"감사한 일."}),s.jsx("input",{value:m.gratitude,placeholder:"오늘 감사한 순간",onChange:C=>y(j=>({...j,gratitude:C.target.value}))})]}),s.jsxs("section",{className:"paper-section brain-dump",children:[s.jsx("h2",{children:"생각 쏟아내기 (Brain Dump)"}),s.jsx("textarea",{value:m.brainDump,placeholder:"머릿속에 떠오르는 모든 일을 한 줄씩 적어두세요.",onChange:C=>y(j=>({...j,brainDump:C.target.value}))})]}),s.jsxs("section",{className:"paper-section time-plan",children:[s.jsxs("div",{className:"section-toolbar",children:[s.jsxs("h2",{children:[s.jsx(wd,{size:18})," TIME PLAN"]}),s.jsxs("button",{onClick:D,children:[s.jsx($n,{size:16})," 블록 추가"]})]}),s.jsxs("div",{className:"timeline-wrap",children:[s.jsxs("div",{className:"minute-labels",children:[s.jsx("span",{}),s.jsx("span",{children:"10"}),s.jsx("span",{children:"20"}),s.jsx("span",{children:"30"}),s.jsx("span",{children:"40"}),s.jsx("span",{children:"50"}),s.jsx("span",{children:"60"})]}),s.jsxs("div",{className:"timeline-grid",style:{height:`${Qa.length*Fo}px`},children:[Qa.map(C=>s.jsx("div",{className:"hour-row",children:s.jsx("span",{children:C})},C)),m.blocks.map(C=>s.jsx(Qd,{block:C,topTitles:R,onChange:j=>E(C.id,j),onRemove:()=>M(C.id)},C.id))]})]})]}),s.jsxs("section",{className:"paper-section memo",children:[s.jsx("h2",{children:"MEMO"}),s.jsx("textarea",{value:m.memo,placeholder:"간단한 메모와 오늘의 회고",onChange:C=>y(j=>({...j,memo:C.target.value}))})]})]})]}),s.jsxs("section",{className:"block-editor",children:[s.jsxs("div",{className:"panel-title-row",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-kicker",children:"Time blocks"}),s.jsx("h2",{children:"일정 블록 편집"})]}),s.jsxs("button",{onClick:D,children:[s.jsx($n,{size:16})," 추가"]})]}),s.jsxs("div",{className:"block-list",children:[m.blocks.length===0?s.jsx("div",{className:"empty-state compact",children:"시간표에 배치할 일을 추가하세요."}):null,m.blocks.map(C=>s.jsxs("article",{className:"block-row",children:[s.jsx("input",{value:C.title,placeholder:"할 일",onChange:j=>E(C.id,{title:j.target.value})}),s.jsx("input",{type:"time",value:C.start,onChange:j=>E(C.id,{start:j.target.value})}),s.jsx("input",{type:"time",value:C.end,onChange:j=>E(C.id,{end:j.target.value})}),s.jsxs("label",{children:[s.jsx("input",{type:"checkbox",checked:!!C.highlight,onChange:j=>E(C.id,{highlight:j.target.checked})}),"TOP3"]}),s.jsx("button",{onClick:()=>C.title&&d(C.title,`Time Plan ${C.start}-${C.end}`),children:s.jsx(gd,{size:15})}),s.jsx("button",{onClick:()=>M(C.id),children:s.jsx(tc,{size:15})})]},C.id))]})]})]})}function Qd({block:m,topTitles:y,onChange:d,onRemove:D}){const E=Math.max(Wa(m.start),Ka),M=Math.max(Wa(m.end),E+30),R=(E-Ka)/60*Fo,C=Math.max((M-E)/60*Fo,24),j=m.highlight||y.some(F=>m.title.includes(F));return s.jsxs("div",{className:`time-block ${j?"priority":""}`,style:{top:`${R}px`,height:`${C}px`},children:[s.jsx("input",{value:m.title,placeholder:"할 일",onChange:F=>d({title:F.target.value})}),s.jsxs("span",{children:[m.start," - ",m.end]}),s.jsx("button",{onClick:D,"aria-label":"시간 블록 삭제",children:s.jsx(tc,{size:13})})]})}function Ga(m,y="manual",d){return{id:lc("block"),title:m,start:"09:00",end:"10:00",source:y,reminderStableId:d}}let Uo=null;async function Dt(m,y){var C;const d=((y==null?void 0:y.method)||"GET").toUpperCase(),D=new Headers(y==null?void 0:y.headers);y!=null&&y.body&&!D.has("content-type")&&D.set("content-type","application/json"),!["GET","HEAD","OPTIONS"].includes(d)&&Uo&&D.set("x-csrf-token",Uo);const E=await fetch(m,{...y,credentials:"same-origin",headers:D}),M=await E.text(),R=M?JSON.parse(M):null;if(!E.ok){const j=((C=R==null?void 0:R.error)==null?void 0:C.message)||(R==null?void 0:R.error)||E.statusText;throw new Error(j)}return R}const nt={setCsrfToken(m){Uo=m},session(){return Dt("/api/auth/session")},login(m,y){return Dt("/api/auth/login",{method:"POST",body:JSON.stringify({email:m,password:y})})},logout(){return Dt("/api/auth/logout",{method:"POST",body:JSON.stringify({})})},health(){return Dt("/api/health")},reminderLists(){return Dt("/api/reminders/lists")},reminders(m){const y=new URLSearchParams;return y.set("lookaheadDays",String(m.lookaheadDays??21)),m.includeUndated&&y.set("includeUndated","1"),m.list&&y.append("list",m.list),Dt(`/api/reminders?${y.toString()}`)},applyReminders(m){return Dt("/api/reminders/apply",{method:"POST",body:JSON.stringify({operations:m})})},googleStatus(){return Dt("/api/google-sync/status")},runGoogleSync(m=!0){return Dt("/api/google-sync/run",{method:"POST",body:JSON.stringify({dryRun:m})})}};function Kd(){const[m,y]=ie.useState(null),[d,D]=ie.useState(""),[E,M]=ie.useState("timebox"),[R,C]=ie.useState(()=>Io()),[j,F]=ie.useState(()=>Bn(Io())),[H,W]=ie.useState(!1),[K,ge]=ie.useState(null),[X,oe]=ie.useState([]),[q,rt]=ie.useState("My"),[lt,Ae]=ie.useState([]),[Ne,We]=ie.useState(!1),[ze,Le]=ie.useState(""),[Re,pt]=ie.useState(null),it=ie.useMemo(()=>j.days[R]||Fl(R),[j.days,R]),$e=(m==null?void 0:m.authenticated)===!0;ie.useEffect(()=>{Be()},[]),ie.useEffect(()=>{!$e||!H||Ad(j)},[$e,j,H]),ie.useEffect(()=>{!$e||!H||j.days[R]||F(T=>({...T,days:{...T.days,[R]:Fl(R)}}))},[R,$e,j.days,H]),ie.useEffect(()=>{if(m){if(m.authenticated){F(Ud()),W(!0),Ee(),re();return}nt.setCsrfToken(null),F(Bn(R)),W(!1),ge(null),oe([]),Ae([])}},[m==null?void 0:m.authenticated]),ie.useEffect(()=>{$e&&re()},[$e,q]);function de(T,V){pt({type:T,message:V}),window.setTimeout(()=>pt(null),4200)}async function Be(){try{const T=await nt.session();nt.setCsrfToken(T.csrfToken),y(T)}catch(T){D(T instanceof Error?T.message:"세션 확인 실패"),y({configured:!1,authenticated:!1,ownerEmail:"leejihun04@gmail.com",csrfToken:null,expiresAt:null})}}async function Ze(T,V){D("");try{const ne=await nt.login(T,V);nt.setCsrfToken(ne.csrfToken),y(ne)}catch(ne){D(ne instanceof Error?ne.message:"로그인 실패")}}async function ot(){try{await nt.logout()}catch{}finally{nt.setCsrfToken(null),y(T=>T&&{...T,authenticated:!1,csrfToken:null,expiresAt:null}),W(!1),ge(null),oe([]),Ae([])}}async function Ee(){try{const T=await nt.health();ge(T)}catch(T){de("error",T instanceof Error?T.message:"상태 확인 실패")}}async function re(){We(!0),Le("");try{const[T,V]=await Promise.all([nt.reminderLists(),nt.reminders({list:q||void 0,includeUndated:!0,lookaheadDays:60})]);oe(T),!q&&T.some(ne=>ne.title==="My")&&rt("My"),Ae(V)}catch(T){const V=T instanceof Error?T.message:"Reminders를 불러오지 못했습니다.";Le(V)}finally{We(!1)}}function N(T){F(V=>{const ne=V.days[R]||Fl(R);return{...V,days:{...V.days,[R]:T(ne)}}})}function $(T,V){F(ne=>({...ne,mandarart:{cells:Bd(ne.mandarart.cells,T,V)}}))}async function P(T,V="Planner Studio",ne=R){var xe;try{const xt=q||((xe=X[0])==null?void 0:xe.title)||"My";await nt.applyReminders([{create:!0,list_title:xt,title:T,notes:V,all_day:!0,due_date:ne}]),de("ok",`Reminders에 저장했습니다: ${T}`),re(),Ee()}catch(xt){de("error",xt instanceof Error?xt.message:"Reminders 저장 실패")}}async function f(T){try{await nt.applyReminders([{stable_id:T.stable_id,complete:!0}]),de("ok",`완료 처리했습니다: ${T.title}`),re()}catch(V){de("error",V instanceof Error?V.message:"완료 처리 실패")}}function g(T){N(V=>{const ne=[...V.topThree],xe=ne.findIndex(xt=>!xt.trim());return ne[xe>=0?xe:2]=T.title,{...V,topThree:ne}}),de("ok","TOP3에 반영했습니다.")}function Q(T){N(V=>({...V,blocks:[...V.blocks,Ga(T.title,"reminder",T.stable_id)]})),de("ok","Time Plan에 블록을 추가했습니다.")}function G(T){N(V=>({...V,blocks:[...V.blocks,Ga(T,"mandarart")]})),M("timebox"),de("ok","만다라트 실행 항목을 Time Plan에 추가했습니다.")}async function J(T){try{const V=await nt.runGoogleSync(T),ne=V.stdout.split(`
|
|
176
176
|
`).find(xe=>xe.includes("Sync summary"))||V.stdout.split(`
|
|
177
|
-
`).slice(-2).join(" ");de("ok",T?`동기화 미리보기 완료 ${ne}`:`Google Tasks 동기화 실행 완료 ${ne}`),Ee()}catch(V){de("error",V instanceof Error?V.message:"Google Tasks 동기화 실패")}}function Z(){const T=new Blob([JSON.stringify(j,null,2)],{type:"application/json"}),V=URL.createObjectURL(T),ne=document.createElement("a");ne.href=V,ne.download=`planner-studio-${R}.json`,ne.click(),URL.revokeObjectURL(V)}function ue(T){const V=new FileReader;V.onload=()=>{var ne;try{const xe=JSON.parse(String(V.result||""));if(xe.version!==1||!xe.days||!((ne=xe.mandarart)!=null&&ne.cells))throw new Error("Planner Studio JSON 형식이 아닙니다.");F(xe),de("ok","JSON을 가져왔습니다.")}catch(xe){de("error",xe instanceof Error?xe.message:"JSON 가져오기 실패")}},V.readAsText(T)}function te(){const T=Bn(R);F(T),de("ok","현재 브라우저의 플래너 데이터를 초기화했습니다.")}const se=ic(j.mandarart.cells).length;return $e?s.jsxs("div",{className:"app-shell",children:[s.jsx(Dd,{view:E,setView:M,date:R,setDate:C,health:K,onRefresh:()=>{Ee(),re()},onExportJson:Z,onImportJson:ue}),s.jsxs("div",{className:"main-column",children:[s.jsxs("header",{className:"topbar",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-kicker",children:"Live planner"}),s.jsx("h2",{children:E==="timebox"?"오늘을 시간 블록으로 실행":"목표를 실행 항목으로 분해"})]}),s.jsxs("div",{className:"topbar-actions",children:[s.jsxs("span",{className:"status-pill",children:[it.blocks.length," blocks"]}),s.jsxs("span",{className:"status-pill",children:[se," actions"]}),s.jsxs("button",{onClick:ot,children:[s.jsx(Cd,{size:15}),"로그아웃"]}),s.jsx("button",{onClick:te,children:"초기화"})]})]}),E==="timebox"?s.jsx(Wd,{plan:it,updatePlan:N,onCreateReminder:P}):s.jsx(Hd,{state:j.mandarart,updateCell:$,onSendToTimebox:G,onCreateReminder:P})]}),s.jsx(Id,{lists:X,reminders:lt,selectedList:q,setSelectedList:rt,loading:Ne,error:ze,onRefresh:re,onAddTop:g,onSchedule:Q,onComplete:f,onCreateQuick:P,onRunGoogleSync:J}),Re?s.jsxs("div",{className:`toast ${Re.type}`,children:[Re.type==="ok"?s.jsx(Ja,{size:18}):s.jsx(kd,{size:18}),s.jsx("span",{children:Re.message})]}):null]}):s.jsx(Gd,{configured:(m==null?void 0:m.configured)??!0,ownerEmail:(m==null?void 0:m.ownerEmail)||"leejihun04@gmail.com",loading:m===null,error:d,onLogin:Ze})}function Gd({configured:m,ownerEmail:y,loading:d,error:D,onLogin:E}){const[M,R]=ie.useState(y),[C,j]=ie.useState(""),[F,H]=ie.useState(!1);ie.useEffect(()=>{R(y)},[y]);async function W(K){K.preventDefault(),H(!0);try{await E(M,C)}finally{H(!1),j("")}}return s.jsx("main",{className:"login-shell",children:s.jsxs("section",{className:"login-card","aria-labelledby":"login-title",children:[s.jsx("div",{className:"login-mark",children:s.jsx(Ed,{size:22})}),s.jsx("span",{className:"section-kicker",children:"Owner access"}),s.jsx("h1",{id:"login-title",children:"Planner Studio"}),s.jsx("p",{children:"등록된 소유자 계정으로만 플래너와 Reminders 연동을 사용할 수 있습니다."}),m?null:s.jsx("p",{className:"login-error",children:"서버 소유자 비밀번호 설정이 필요합니다."}),D?s.jsx("p",{className:"login-error",children:D}):null,s.jsxs("form",{onSubmit:W,className:"login-form",children:[s.jsxs("label",{children:[s.jsx("span",{children:"아이디"}),s.jsx("input",{type:"email",value:M,autoComplete:"username",onChange:K=>R(K.target.value),required:!0})]}),s.jsxs("label",{children:[s.jsx("span",{children:"비밀번호"}),s.jsx("input",{type:"password",value:C,autoComplete:"current-password",onChange:K=>j(K.target.value),required:!0})]}),s.jsx("button",{type:"submit",disabled:d||F||!m,children:d?"세션 확인 중":F?"로그인 중":"로그인"})]})]})})}pd.createRoot(document.getElementById("root")).render(s.jsx(od.StrictMode,{children:s.jsx(Kd,{})}));
|
|
177
|
+
`).slice(-2).join(" ");de("ok",T?`동기화 미리보기 완료 ${ne}`:`Google Tasks 동기화 실행 완료 ${ne}`),Ee()}catch(V){de("error",V instanceof Error?V.message:"Google Tasks 동기화 실패")}}function Z(){const T=new Blob([JSON.stringify(j,null,2)],{type:"application/json"}),V=URL.createObjectURL(T),ne=document.createElement("a");ne.href=V,ne.download=`planner-studio-${R}.json`,ne.click(),URL.revokeObjectURL(V)}function ue(T){const V=new FileReader;V.onload=()=>{var ne;try{const xe=JSON.parse(String(V.result||""));if(xe.version!==1||!xe.days||!((ne=xe.mandarart)!=null&&ne.cells))throw new Error("Planner Studio JSON 형식이 아닙니다.");F(xe),de("ok","JSON을 가져왔습니다.")}catch(xe){de("error",xe instanceof Error?xe.message:"JSON 가져오기 실패")}},V.readAsText(T)}function te(){const T=Bn(R);F(T),de("ok","현재 브라우저의 플래너 데이터를 초기화했습니다.")}const se=ic(j.mandarart.cells).length;return $e?s.jsxs("div",{className:"app-shell",children:[s.jsx(Dd,{view:E,setView:M,date:R,setDate:C,health:K,onRefresh:()=>{Ee(),re()},onExportJson:Z,onImportJson:ue}),s.jsxs("div",{className:"main-column",children:[s.jsxs("header",{className:"topbar",children:[s.jsxs("div",{children:[s.jsx("span",{className:"section-kicker",children:"Live planner"}),s.jsx("h2",{children:E==="timebox"?"오늘을 시간 블록으로 실행":"목표를 실행 항목으로 분해"})]}),s.jsxs("div",{className:"topbar-actions",children:[s.jsxs("span",{className:"status-pill",children:[it.blocks.length," blocks"]}),s.jsxs("span",{className:"status-pill",children:[se," actions"]}),s.jsxs("button",{className:"subtle-button",onClick:ot,children:[s.jsx(Cd,{size:15}),"로그아웃"]}),s.jsx("button",{className:"subtle-button",onClick:te,children:"초기화"})]})]}),E==="timebox"?s.jsx(Wd,{plan:it,updatePlan:N,onCreateReminder:P}):s.jsx(Hd,{state:j.mandarart,updateCell:$,onSendToTimebox:G,onCreateReminder:P})]}),s.jsx(Id,{lists:X,reminders:lt,selectedList:q,setSelectedList:rt,loading:Ne,error:ze,onRefresh:re,onAddTop:g,onSchedule:Q,onComplete:f,onCreateQuick:P,onRunGoogleSync:J}),Re?s.jsxs("div",{className:`toast ${Re.type}`,children:[Re.type==="ok"?s.jsx(Ja,{size:18}):s.jsx(kd,{size:18}),s.jsx("span",{children:Re.message})]}):null]}):s.jsx(Gd,{configured:(m==null?void 0:m.configured)??!0,ownerEmail:(m==null?void 0:m.ownerEmail)||"leejihun04@gmail.com",loading:m===null,error:d,onLogin:Ze})}function Gd({configured:m,ownerEmail:y,loading:d,error:D,onLogin:E}){const[M,R]=ie.useState(y),[C,j]=ie.useState(""),[F,H]=ie.useState(!1);ie.useEffect(()=>{R(y)},[y]);async function W(K){K.preventDefault(),H(!0);try{await E(M,C)}finally{H(!1),j("")}}return s.jsx("main",{className:"login-shell",children:s.jsxs("section",{className:"login-card","aria-labelledby":"login-title",children:[s.jsx("div",{className:"login-mark",children:s.jsx(Ed,{size:22})}),s.jsx("span",{className:"section-kicker",children:"Owner access"}),s.jsx("h1",{id:"login-title",children:"Planner Studio"}),s.jsx("p",{children:"등록된 소유자 계정으로만 플래너와 Reminders 연동을 사용할 수 있습니다."}),m?null:s.jsx("p",{className:"login-error",children:"서버 소유자 비밀번호 설정이 필요합니다."}),D?s.jsx("p",{className:"login-error",children:D}):null,s.jsxs("form",{onSubmit:W,className:"login-form",children:[s.jsxs("label",{children:[s.jsx("span",{children:"아이디"}),s.jsx("input",{type:"email",value:M,autoComplete:"username",onChange:K=>R(K.target.value),required:!0})]}),s.jsxs("label",{children:[s.jsx("span",{children:"비밀번호"}),s.jsx("input",{type:"password",value:C,autoComplete:"current-password",onChange:K=>j(K.target.value),required:!0})]}),s.jsx("button",{type:"submit",disabled:d||F||!m,children:d?"세션 확인 중":F?"로그인 중":"로그인"})]})]})})}pd.createRoot(document.getElementById("root")).render(s.jsx(od.StrictMode,{children:s.jsx(Kd,{})}));
|
package/dist/index.html
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<meta name="color-scheme" content="light" />
|
|
7
7
|
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='10' fill='%231f1f23'/%3E%3Cpath d='M16 18h32M16 30h32M16 42h20' stroke='white' stroke-width='5' stroke-linecap='round'/%3E%3C/svg%3E" />
|
|
8
8
|
<title>Planner Studio</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-YuLQdXWX.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-DrkqcCB3.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
package/server.mjs
CHANGED
|
@@ -362,11 +362,17 @@ function isUnsafeMethod(method) {
|
|
|
362
362
|
return !["GET", "HEAD", "OPTIONS"].includes(method || "GET");
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
-
function originAllowed(req) {
|
|
365
|
+
function originAllowed(req, options = {}) {
|
|
366
366
|
const origin = req.headers.origin;
|
|
367
367
|
if (!origin) return true;
|
|
368
368
|
const normalized = normalizeOrigin(origin);
|
|
369
|
-
if (!normalized)
|
|
369
|
+
if (!normalized) {
|
|
370
|
+
return Boolean(
|
|
371
|
+
options.allowNullOrigin
|
|
372
|
+
&& String(origin).trim() === "null"
|
|
373
|
+
&& trustedOrigins.has(requestOrigin(req))
|
|
374
|
+
);
|
|
375
|
+
}
|
|
370
376
|
return trustedOrigins.has(normalized) || normalized === requestOrigin(req);
|
|
371
377
|
}
|
|
372
378
|
|
|
@@ -503,7 +509,142 @@ function sendAuthCss(res) {
|
|
|
503
509
|
"content-type": "text/css; charset=utf-8",
|
|
504
510
|
"cache-control": "no-store"
|
|
505
511
|
}));
|
|
506
|
-
res.end(`:root
|
|
512
|
+
res.end(`:root {
|
|
513
|
+
font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Noto Sans KR", "Segoe UI", sans-serif;
|
|
514
|
+
color: #1f1f23;
|
|
515
|
+
background: #f1f4f9;
|
|
516
|
+
--ink: #1f1f23;
|
|
517
|
+
--muted: #72737a;
|
|
518
|
+
--line: #d7dbe3;
|
|
519
|
+
--cobalt: #315ddc;
|
|
520
|
+
--green: #26886e;
|
|
521
|
+
--yellow: #f4c84f;
|
|
522
|
+
--focus: rgba(49, 93, 220, 0.24);
|
|
523
|
+
}
|
|
524
|
+
* {
|
|
525
|
+
box-sizing: border-box;
|
|
526
|
+
}
|
|
527
|
+
body {
|
|
528
|
+
margin: 0;
|
|
529
|
+
min-height: 100vh;
|
|
530
|
+
background:
|
|
531
|
+
linear-gradient(90deg, rgba(31, 31, 35, 0.035) 1px, transparent 1px),
|
|
532
|
+
linear-gradient(rgba(31, 31, 35, 0.035) 1px, transparent 1px),
|
|
533
|
+
#f1f4f9;
|
|
534
|
+
background-size: 34px 34px;
|
|
535
|
+
}
|
|
536
|
+
button,
|
|
537
|
+
input {
|
|
538
|
+
font: inherit;
|
|
539
|
+
}
|
|
540
|
+
.login-shell {
|
|
541
|
+
min-height: 100vh;
|
|
542
|
+
display: grid;
|
|
543
|
+
place-items: center;
|
|
544
|
+
padding: clamp(20px, 5vw, 48px);
|
|
545
|
+
}
|
|
546
|
+
.login-card {
|
|
547
|
+
width: min(430px, 100%);
|
|
548
|
+
background: #fff;
|
|
549
|
+
border: 1px solid var(--line);
|
|
550
|
+
border-radius: 8px;
|
|
551
|
+
padding: 30px;
|
|
552
|
+
box-shadow: 0 18px 48px rgba(31, 35, 42, 0.12);
|
|
553
|
+
position: relative;
|
|
554
|
+
overflow: hidden;
|
|
555
|
+
}
|
|
556
|
+
.login-card::before {
|
|
557
|
+
content: "";
|
|
558
|
+
position: absolute;
|
|
559
|
+
inset: 0 0 auto;
|
|
560
|
+
height: 4px;
|
|
561
|
+
background: linear-gradient(90deg, var(--cobalt), var(--green), var(--yellow));
|
|
562
|
+
}
|
|
563
|
+
.mark {
|
|
564
|
+
width: 46px;
|
|
565
|
+
height: 46px;
|
|
566
|
+
border: 1px solid #c8d3ef;
|
|
567
|
+
border-radius: 8px;
|
|
568
|
+
display: grid;
|
|
569
|
+
place-items: center;
|
|
570
|
+
font-weight: 900;
|
|
571
|
+
margin-bottom: 18px;
|
|
572
|
+
background: #f5f7fc;
|
|
573
|
+
color: var(--cobalt);
|
|
574
|
+
}
|
|
575
|
+
.eyebrow {
|
|
576
|
+
margin: 0 0 6px;
|
|
577
|
+
color: var(--muted);
|
|
578
|
+
font-size: 12px;
|
|
579
|
+
font-weight: 850;
|
|
580
|
+
text-transform: uppercase;
|
|
581
|
+
letter-spacing: 0;
|
|
582
|
+
}
|
|
583
|
+
h1 {
|
|
584
|
+
margin: 0;
|
|
585
|
+
font-size: 30px;
|
|
586
|
+
line-height: 1.08;
|
|
587
|
+
}
|
|
588
|
+
.copy {
|
|
589
|
+
color: var(--muted);
|
|
590
|
+
line-height: 1.5;
|
|
591
|
+
margin: 12px 0 20px;
|
|
592
|
+
}
|
|
593
|
+
.alert {
|
|
594
|
+
border: 1px solid #f0b8b8;
|
|
595
|
+
background: #fff6f6;
|
|
596
|
+
color: #9c2f2f;
|
|
597
|
+
border-radius: 8px;
|
|
598
|
+
padding: 10px 12px;
|
|
599
|
+
font-size: 13px;
|
|
600
|
+
line-height: 1.45;
|
|
601
|
+
}
|
|
602
|
+
form {
|
|
603
|
+
display: grid;
|
|
604
|
+
gap: 12px;
|
|
605
|
+
}
|
|
606
|
+
label {
|
|
607
|
+
display: grid;
|
|
608
|
+
gap: 6px;
|
|
609
|
+
font-size: 13px;
|
|
610
|
+
font-weight: 800;
|
|
611
|
+
color: #4f5159;
|
|
612
|
+
}
|
|
613
|
+
input,
|
|
614
|
+
button {
|
|
615
|
+
border-radius: 8px;
|
|
616
|
+
min-height: 42px;
|
|
617
|
+
}
|
|
618
|
+
input {
|
|
619
|
+
border: 1px solid var(--line);
|
|
620
|
+
padding: 0 12px;
|
|
621
|
+
color: var(--ink);
|
|
622
|
+
background: #fbfcff;
|
|
623
|
+
}
|
|
624
|
+
input:focus-visible,
|
|
625
|
+
button:focus-visible {
|
|
626
|
+
outline: 3px solid var(--focus);
|
|
627
|
+
outline-offset: 2px;
|
|
628
|
+
}
|
|
629
|
+
button {
|
|
630
|
+
border: 1px solid var(--ink);
|
|
631
|
+
background: var(--ink);
|
|
632
|
+
color: #fff;
|
|
633
|
+
font-weight: 850;
|
|
634
|
+
cursor: pointer;
|
|
635
|
+
box-shadow: 0 10px 22px rgba(31, 31, 35, 0.16);
|
|
636
|
+
}
|
|
637
|
+
button:hover:not(:disabled) {
|
|
638
|
+
background: #111215;
|
|
639
|
+
border-color: #111215;
|
|
640
|
+
}
|
|
641
|
+
button:disabled {
|
|
642
|
+
opacity: .5;
|
|
643
|
+
cursor: not-allowed;
|
|
644
|
+
}
|
|
645
|
+
code {
|
|
646
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
647
|
+
}`);
|
|
507
648
|
}
|
|
508
649
|
|
|
509
650
|
async function handleAuth(req, res, url) {
|
|
@@ -524,9 +665,10 @@ async function handleAuth(req, res, url) {
|
|
|
524
665
|
else sendJson(res, 503, { ok: false, error: "Owner authentication is not configured" }, corsHeaders(req));
|
|
525
666
|
return true;
|
|
526
667
|
}
|
|
527
|
-
if (!originAllowed(req)) {
|
|
668
|
+
if (!originAllowed(req, { allowNullOrigin: formLogin })) {
|
|
528
669
|
logOriginBlocked(req, url.pathname);
|
|
529
|
-
|
|
670
|
+
if (formLogin) redirect(res, "/login?error=1");
|
|
671
|
+
else sendJson(res, 403, { ok: false, error: "Origin not allowed" }, corsHeaders(req));
|
|
530
672
|
return true;
|
|
531
673
|
}
|
|
532
674
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
:root{color:#1f1f23;background:#eef2f7;font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Apple SD Gothic Neo,Noto Sans KR,Segoe UI,sans-serif;font-synthesis:none;text-rendering:optimizeLegibility;--ink: #1f1f23;--muted: #72737a;--line: #d7dbe3;--heavy-line: #25252a;--paper: #ffffff;--panel: #f8fafc;--cobalt: #315ddc;--green: #26886e;--yellow: #f4c84f;--danger: #be3a3a}*{box-sizing:border-box}body{margin:0;min-width:320px;min-height:100vh}button,input,select,textarea{font:inherit}button{border:1px solid var(--line);border-radius:8px;background:#fff;color:var(--ink);cursor:pointer;min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:7px;padding:0 12px;font-size:13px;font-weight:750}button:disabled{cursor:not-allowed;opacity:.56}input,select,textarea{border:1px solid var(--line);border-radius:8px;color:var(--ink);background:#fff;min-height:34px}textarea{resize:vertical}.login-shell{min-height:100vh;display:grid;place-items:center;padding:24px}.login-card{width:min(430px,100%);background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:0 20px 50px #181c2624;padding:28px}.login-mark{width:46px;height:46px;border:2px solid var(--ink);border-radius:8px;display:grid;place-items:center;margin-bottom:18px}.login-card h1{margin:4px 0 0;font-size:30px;line-height:1.08}.login-card p{margin:12px 0 20px;color:var(--muted);line-height:1.5}.login-error{border:1px solid #f0b8b8;background:#fff6f6;color:#9c2f2f!important;border-radius:8px;padding:10px 12px;font-size:13px}.login-form{display:grid;gap:12px}.login-form label{display:grid;gap:6px;font-size:13px;font-weight:800;color:#4f5159}.login-form input{min-height:42px;padding:0 12px}.login-form button{min-height:42px;background:var(--ink);color:#fff;border-color:var(--ink)}.app-shell{display:grid;grid-template-columns:248px minmax(700px,1fr) 348px;gap:14px;min-height:100vh;padding:14px}.status-rail,.reminder-panel,.block-editor,.mandarart-actions-panel{background:var(--panel);border:1px solid var(--line);border-radius:8px}.status-rail{display:flex;flex-direction:column;gap:14px;padding:14px;position:sticky;top:14px;height:calc(100vh - 28px);overflow:auto}.brand-block{display:grid;grid-template-columns:42px 1fr;gap:10px;align-items:center}.brand-mark{width:42px;height:42px;border:2px solid var(--ink);border-radius:8px;display:grid;place-items:center;font-weight:900;background:#fff}.brand-block strong,.brand-block span{display:block}.brand-block strong{font-size:15px}.brand-block span,.rail-note,.sync-panel dd,.sync-panel dt,.section-kicker{color:var(--muted);font-size:12px}.date-card{display:grid;gap:8px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px}.date-card>span,.sync-heading,.panel-title-row{display:flex;align-items:center;gap:8px}.date-card input{width:100%;padding:0 10px}.date-card strong{font-size:22px}.mode-switch{display:grid;gap:8px}.mode-switch button{justify-content:flex-start;min-height:42px}.mode-switch button.active{border-color:var(--ink);background:var(--ink);color:#fff}.sync-panel{background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px}.sync-heading{justify-content:space-between;margin-bottom:10px}.sync-heading strong{margin-right:auto}.sync-panel dl{display:grid;gap:9px;margin:0}.sync-panel dl>div{display:flex;align-items:baseline;justify-content:space-between;gap:8px}.sync-panel dd{margin:0;color:var(--ink);text-align:right}.rail-actions{display:grid;gap:8px}.rail-actions button,.file-button{justify-content:flex-start;min-height:38px}.file-button{border:1px solid var(--line);border-radius:8px;background:#fff;padding:0 12px;display:flex;align-items:center;gap:7px;cursor:pointer;font-size:13px;font-weight:750}.file-button input{display:none}.rail-note{margin-top:auto;display:flex;gap:8px;line-height:1.45}.main-column{min-width:0;display:grid;grid-template-rows:auto minmax(0,1fr);gap:12px}.topbar{min-height:68px;display:flex;align-items:center;justify-content:space-between;gap:12px;background:#fff;border:1px solid var(--line);border-radius:8px;padding:12px 14px}.topbar h2,.panel-title-row h2{margin:1px 0 0;font-size:20px;line-height:1.15}.topbar-actions{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:flex-end}.status-pill{border:1px solid var(--line);border-radius:999px;background:#f9fbff;padding:8px 10px;font-size:12px;font-weight:800}.workspace{min-width:0;min-height:0}.timebox-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}.planner-sheet{background:var(--paper);border:1px solid var(--line);border-radius:8px;box-shadow:0 14px 34px #23272f14}.timebox-sheet{padding:28px;min-height:calc(100vh - 108px)}.sheet-header{display:flex;justify-content:space-between;gap:18px;align-items:flex-start;margin-bottom:24px}.sheet-header h1{margin:0;font-size:clamp(34px,4vw,66px);line-height:.96;font-weight:950}.sheet-header span,.date-line span{color:var(--muted)}.date-line{display:flex;align-items:baseline;gap:12px;border-bottom:2px solid var(--ink);min-width:240px;padding-bottom:8px;font-size:18px}.date-line strong{font-size:24px;font-weight:900}.timebox-grid-layout{display:grid;grid-template-columns:minmax(260px,.93fr) minmax(360px,1fr);grid-template-rows:auto auto 1fr auto;gap:18px 24px;grid-template-areas:"top gratitude" "brain time" "brain time" "brain memo"}.paper-section{min-width:0}.paper-section h2{margin:0 0 12px;font-size:18px;font-weight:900;border-bottom:2px solid var(--ink);padding-bottom:10px;display:flex;align-items:center;gap:8px}.top-three{grid-area:top}.gratitude{grid-area:gratitude}.brain-dump{grid-area:brain}.time-plan{grid-area:time}.memo{grid-area:memo}.top-input{display:grid;grid-template-columns:24px minmax(0,1fr) 36px;gap:8px;align-items:center;border-bottom:1px solid var(--line);min-height:46px}.top-input span{font-weight:900;color:var(--cobalt)}.top-input input,.gratitude input{border:0;border-radius:0;min-width:0}.top-input input:focus,.gratitude input:focus,.brain-dump textarea:focus,.memo textarea:focus,.time-block input:focus,.mandarart-cell:focus{outline:2px solid rgba(49,93,220,.2);outline-offset:-2px}.top-input button{width:32px;min-height:30px;padding:0}.brain-dump textarea{width:100%;min-height:720px;border:0;border-radius:0;line-height:30px;background-image:linear-gradient(#eef1f5 1px,transparent 1px),linear-gradient(90deg,#f4f6f8 1px,transparent 1px);background-size:32px 30px;padding:10px 8px}.gratitude input{width:100%;border-bottom:1px solid var(--line)}.section-toolbar{display:flex;justify-content:space-between;gap:10px;align-items:flex-start}.section-toolbar h2{flex:1}.timeline-wrap{position:relative}.minute-labels{display:grid;grid-template-columns:44px repeat(6,1fr);color:#b5bac5;font-size:11px;border-bottom:1px solid var(--line)}.minute-labels span{text-align:right;padding:0 4px 3px 0}.timeline-grid{position:relative;border-bottom:2px solid var(--ink);background:repeating-linear-gradient(to right,transparent 0,transparent calc((100% - 44px) / 6 - 1px),#e9edf2 calc((100% - 44px) / 6)),linear-gradient(to right,transparent 0 44px,#f8fafc 44px 100%)}.hour-row{display:grid;grid-template-columns:44px 1fr;height:46px;border-bottom:1px solid #d2d6de}.hour-row span{font-weight:800;padding-top:7px;color:var(--ink)}.time-block{position:absolute;left:50px;right:8px;border:1px solid rgba(49,93,220,.35);border-left:4px solid var(--cobalt);background:#315ddc14;border-radius:8px;display:grid;grid-template-columns:minmax(0,1fr) auto 24px;align-items:center;gap:8px;padding:5px 6px;overflow:hidden}.time-block.priority{border-color:#f4c84fb8;border-left-color:var(--yellow);background:#f4c84f29}.time-block input{min-width:0;min-height:24px;border:0;background:transparent;font-weight:800}.time-block span{color:var(--muted);font-size:12px;white-space:nowrap}.time-block button{width:22px;min-height:22px;padding:0;background:transparent}.memo textarea{min-height:142px;width:100%;border:0;border-radius:0;line-height:34px;background-image:linear-gradient(#d8dce3 1px,transparent 1px);background-size:100% 34px;padding:6px 0}.block-editor{padding:14px;overflow:auto}.panel-title-row{justify-content:space-between;margin-bottom:14px}.block-list,.action-list,.reminder-list{display:grid;gap:8px}.block-row{display:grid;grid-template-columns:minmax(140px,1fr) 88px 88px 74px 34px 34px;gap:6px;align-items:center;background:#fff;border:1px solid var(--line);border-radius:8px;padding:8px}.block-row input{min-width:0;padding:0 8px}.block-row label{display:flex;align-items:center;gap:4px;font-size:12px;color:var(--muted)}.block-row button{width:32px;padding:0}.reminder-panel{position:sticky;top:14px;height:calc(100vh - 28px);padding:14px;display:flex;flex-direction:column;min-width:0}.select-row{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;margin-bottom:10px}.select-row label{display:grid;gap:4px;color:var(--muted);font-size:12px}.select-row select{width:100%;padding:0 8px}.metric-strip{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:10px}.metric-strip div{background:#fff;border:1px solid var(--line);border-radius:8px;padding:10px}.metric-strip strong,.metric-strip span{display:block}.metric-strip strong{font-size:22px;line-height:1}.metric-strip span{color:var(--muted);font-size:12px;margin-top:3px}.quick-create{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;margin-bottom:10px}.quick-create input{min-width:0;padding:0 10px}.error-text{margin:0 0 10px;color:var(--danger);font-size:13px}.reminder-list{overflow:auto;padding-right:2px;flex:1}.reminder-row,.action-row{background:#fff;border:1px solid var(--line);border-radius:8px;padding:10px;display:grid;gap:9px}.reminder-row strong,.reminder-row span{display:block}.reminder-row strong{line-height:1.25;overflow-wrap:anywhere}.reminder-row span{color:var(--muted);font-size:12px;margin-top:4px}.row-actions{display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.row-actions button,.action-row button{min-height:30px;padding:0}.google-sync-box{margin-top:12px;border-top:1px solid var(--line);padding-top:12px;display:grid;gap:8px}.google-sync-box span{color:var(--muted);font-size:12px;line-height:1.42}.google-sync-box div{display:grid;grid-template-columns:1fr 1fr;gap:8px}.empty-state{background:#fff;border:1px dashed #c9ced7;border-radius:8px;padding:18px;display:grid;gap:6px;color:var(--muted);text-align:center}.empty-state strong{color:var(--ink)}.empty-state.compact{padding:14px;font-size:13px}.mandarart-workspace{display:grid;grid-template-columns:minmax(0,1fr);gap:12px}.mandarart-sheet{padding:28px}.mandarart-grid{width:min(100%,860px);aspect-ratio:1;margin:0 auto;display:grid;grid-template-columns:repeat(9,minmax(0,1fr));grid-template-rows:repeat(9,minmax(0,1fr));border:2px solid var(--heavy-line)}.mandarart-cell{width:100%;height:100%;min-height:0;border:0;border-right:1px solid var(--heavy-line);border-bottom:1px solid var(--heavy-line);border-radius:0;resize:none;padding:8px;text-align:center;display:block;font-size:clamp(11px,.95vw,14px);line-height:1.28;overflow:hidden}.mandarart-cell.theme-cell{background:#e8e8e8;font-weight:850}.mandarart-cell.main-goal{background:var(--ink);color:#fff;font-weight:900}.mandarart-cell.thick-left{border-left:2px solid var(--heavy-line)}.mandarart-cell.thick-top{border-top:2px solid var(--heavy-line)}.mandarart-cell.thick-right{border-right:2px solid var(--heavy-line)}.mandarart-cell.thick-bottom{border-bottom:2px solid var(--heavy-line)}.mandarart-actions-panel{padding:14px;overflow:auto}.mandarart-actions-panel p{margin:0 0 14px;color:var(--muted);font-size:13px;line-height:1.5}.action-row{grid-template-columns:minmax(0,1fr) auto;align-items:center}.action-row span{overflow-wrap:anywhere;line-height:1.35}.action-row div{display:flex;gap:6px}.wide-action{width:100%;margin-top:12px;background:var(--ink);color:#fff;border-color:var(--ink)}.icon-button{width:34px;min-height:34px;padding:0}.toast{position:fixed;left:50%;bottom:18px;transform:translate(-50%);display:flex;align-items:center;gap:8px;max-width:min(560px,calc(100vw - 28px));background:#fff;border:1px solid var(--line);border-radius:8px;box-shadow:0 16px 40px #14171e29;padding:11px 14px;z-index:20;font-size:13px;font-weight:750}.toast.ok svg{color:var(--green)}.toast.error svg{color:var(--danger)}.spin{animation:spin .9s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}@media(max-width:1240px){.app-shell{grid-template-columns:220px minmax(620px,1fr)}.reminder-panel{grid-column:1 / -1;position:static;height:auto;max-height:520px}}@media(max-width:920px){.app-shell,.timebox-workspace,.mandarart-workspace,.timebox-grid-layout{grid-template-columns:1fr}.app-shell{padding:8px}.status-rail{position:static;height:auto}.timebox-grid-layout{grid-template-areas:"top" "gratitude" "brain" "time" "memo"}.timebox-sheet,.mandarart-sheet{padding:18px}.brain-dump textarea{min-height:260px}.block-row{grid-template-columns:1fr 1fr}.mandarart-grid{width:100%}}@media print{body{background:#fff}.status-rail,.reminder-panel,.topbar,.block-editor,.mandarart-actions-panel,.toast{display:none}.app-shell,.main-column,.workspace,.timebox-workspace,.mandarart-workspace{display:block;padding:0}.planner-sheet{border:0;box-shadow:none;min-height:auto}}
|