gyojeong 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +3 -0
- package/README.md +43 -7
- package/apps/review-web/dist/assets/{feedback-contract-CMrinGBH.js → feedback-contract-_524rCEj.js} +1 -1
- package/apps/review-web/dist/assets/feedback-wrapper-BPcSoKgo.js +3 -0
- package/apps/review-web/dist/assets/feedback-wrapper-gbapxzQa.css +1 -0
- package/apps/review-web/dist/assets/{index-DOaH7bry.css → index-CxnczdwI.css} +1 -1
- package/apps/review-web/dist/assets/index-DlzIyqhR.js +25 -0
- package/apps/review-web/dist/feedback-wrapper.html +3 -3
- package/apps/review-web/dist/index.html +3 -3
- package/bin/gyojeong.mjs +2 -1
- package/package.json +1 -1
- package/scripts/check.mjs +168 -29
- package/scripts/review-cli.mjs +1248 -136
- package/apps/review-web/dist/assets/feedback-wrapper-Bhgs7aQS.js +0 -3
- package/apps/review-web/dist/assets/feedback-wrapper-DWKFHwo5.css +0 -1
- package/apps/review-web/dist/assets/index-CY49dpth.js +0 -17
package/NOTICE
CHANGED
package/README.md
CHANGED
|
@@ -14,18 +14,21 @@ npx --yes skills@1.5.21 add voidmatcha/gyojeong -g --all
|
|
|
14
14
|
|
|
15
15
|
| Command | What it does |
|
|
16
16
|
| --- | --- |
|
|
17
|
-
| `gyojeong check --source <file> --draft <file>` | Checks protected tokens, numbers, modality, and optional terms. Exit 0 pass, 1 warn, 2 fail, 3 undecidable |
|
|
17
|
+
| `gyojeong check --source <file> --draft <file>` | Checks protected tokens, numbers, modality, and optional terms. Exit 0 pass, 1 warn, 2 fail (outranks undecidable), 3 undecidable, 64 usage error |
|
|
18
18
|
| `gyojeong publish --source <file> --draft <file> [--mode translate\|polish] [--attach <image>]... [--surface <json>]` | Stores a review locally, starts the review server if needed, prints the link (`url`). A `--surface` capture also prints `surface_url`, the page view |
|
|
19
19
|
| `gyojeong feedback --review <id>` | Prints the review's feedback as JSON, with each item's `draft_version` and any `earlier_drafts` it points into |
|
|
20
20
|
| `gyojeong revise --review <id> --draft <file>` | Replaces the draft under review and re-runs the checker (refused for a captured page review) |
|
|
21
|
-
| `gyojeong processed --review <id> --ids <id,...>` | Marks feedback as applied |
|
|
21
|
+
| `gyojeong processed --review <id> --ids <id,...>` | Marks feedback as applied locally; durable-rule curation remains pending |
|
|
22
22
|
| `gyojeong verify-feedback --review <id> --draft <file>` | Checks whether a revised file applied the pending feedback: `resolved`, `unresolved`, or `not_verifiable` per item |
|
|
23
23
|
| `gyojeong url --review <id>` | Prints the review link, starting the server if it stopped |
|
|
24
24
|
| `gyojeong list` / `gyojeong discard --review <id>` | Lists open reviews / deletes one |
|
|
25
|
-
| `gyojeong serve` | Runs the review server in the foreground on `
|
|
25
|
+
| `gyojeong serve [--port <n>]` | Runs the review server in the foreground on `127.0.0.1`, port 8377 by default (`--port` or `GYOJEONG_REVIEW_PORT`); if taken, tries up to 20 ports upward |
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
The other commands exit 0 on success, 1 on a runtime error, and 64 on a usage error, as does an
|
|
28
|
+
unknown command.
|
|
29
|
+
|
|
30
|
+
Reviews (source, draft, attachments, and feedback) live in `~/.local/share/gyojeong/runs/`
|
|
31
|
+
(owner-only permissions; set `GYOJEONG_HOME` to move them) and are deleted when the review ends. The server listens on 127.0.0.1 only and exits on its own when idle: 48 hours
|
|
29
32
|
after the last request while a review is open, 30 minutes when none is, 5 minutes after the last
|
|
30
33
|
review ends. Change these in `~/.config/gyojeong/config.json`:
|
|
31
34
|
|
|
@@ -33,7 +36,40 @@ review ends. Change these in `~/.config/gyojeong/config.json`:
|
|
|
33
36
|
{ "review_server": { "idle_open_hours": 48, "idle_empty_minutes": 30, "idle_after_finish_minutes": 5 } }
|
|
34
37
|
```
|
|
35
38
|
|
|
36
|
-
Requirements: Node 20 or later. `npx` needs npm access the first time it downloads the CLI
|
|
37
|
-
|
|
39
|
+
Requirements: Node 20 or later. `npx` needs npm access the first time it downloads the CLI. Local
|
|
40
|
+
checking and review need no GitHub account. Only distilled rules and candidates remain in the
|
|
41
|
+
current secret Gist; exact correction evidence is recoverable from its Git history. Rule sync
|
|
42
|
+
requires Git and authenticated GitHub CLI (`gh`); source, draft, and attachments stay local.
|
|
43
|
+
Install `gh` with `brew install gh` (macOS),
|
|
44
|
+
`winget install --id GitHub.cli` (Windows), or `sudo apt install gh` where that package is available,
|
|
45
|
+
then run `gh auth login` and verify with `gh auth status`. Other platforms should use
|
|
46
|
+
<https://cli.github.com/>.
|
|
47
|
+
|
|
48
|
+
On first rule use, `gyojeong rules` reports whether history onboarding is required.
|
|
49
|
+
Use `gyojeong rules --compact true` for just the Gist ID, onboarding status, and active writing
|
|
50
|
+
rules. Its default output also includes counts and IDs, not raw candidate corrections. Use
|
|
51
|
+
`gyojeong rules --candidates true` only when you need to review pending evidence.
|
|
52
|
+
`gyojeong rules --history all` previews short user-authored correction messages from local Claude
|
|
53
|
+
and Codex sessions without uploading them. Equivalent text is grouped with distinct-session
|
|
54
|
+
occurrence evidence. An agent can then store only concise derived rules with
|
|
55
|
+
`gyojeong rules --session <json> --complete-onboarding true`.
|
|
56
|
+
Session entries need a concise, standalone `rule` (at most 240 characters); raw before/after text
|
|
57
|
+
is archived, not retained in the live index. After processing browser feedback, run
|
|
58
|
+
`gyojeong rules --review <id>` to preview local items, then
|
|
59
|
+
`gyojeong rules --review <id> --curate <private-json-file>` with one decision for every item:
|
|
60
|
+
`active`, `candidate` with a short `rule`, or `discard`. Equivalent candidates can share the same
|
|
61
|
+
`rule` with `repeatable: true`; three distinct reviews or sessions then promote it. Exact text
|
|
62
|
+
repetition alone does not. Add `context` only for a necessary condition or exception, and
|
|
63
|
+
`mappings` only for concise term pairs such as `[{"from":"셸","to":"shell"}]`.
|
|
64
|
+
|
|
65
|
+
For multiple machines, containers, or agent homes, create the Gist in the first environment and
|
|
66
|
+
connect each additional environment once with
|
|
67
|
+
`GYOJEONG_GIST_ID=<gist-id> gyojeong rules`. Do not copy the whole config: onboarding completion is
|
|
68
|
+
keyed by the local machine and Claude/Codex history roots, so each environment scans its own history
|
|
69
|
+
while sharing the same Gist.
|
|
70
|
+
If review must happen on another machine, run
|
|
71
|
+
`gyojeong rules --history all --export <private-file>` on the source and
|
|
72
|
+
`gyojeong rules --history-import <private-file>` on the reviewer. The file contains only short
|
|
73
|
+
correction candidates, must be transferred privately, and should be deleted after processing.
|
|
38
74
|
|
|
39
75
|
License: Apache-2.0.
|
package/apps/review-web/dist/assets/{feedback-contract-CMrinGBH.js → feedback-contract-_524rCEj.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=()=>{if(typeof crypto<`u`&&typeof crypto.randomUUID==`function`)return crypto.randomUUID();let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=(e[6]??0)&15|64,e[8]=(e[8]??0)&63|128;let t=Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`},t=(e,t)=>{let n=e.body;return[n?.message??(e instanceof Error?e.message:t),n?.next_action].filter(Boolean).join(` `)},n=(e,t,n,r=!1)=>({code:e,message:t,next_action:n,retryable:r}),r=(e,t,r,i,a=!1)=>Object.assign(Error(t),{status:i,body:n(e,t,r,a)}),i=async e=>{let t;try{t=await e.json()}catch{t=void 0}throw Object.assign(Error(t?.message??`로컬 검수 서버 요청에 실패했습니다.`),{status:e.status,body:t})},a=async(e,t)=>{try{let n=await fetch(e,{cache:`no-store`,...t});return n.ok||await i(n),n}catch(e){throw e.body?e:r(`local_server_unavailable`,`로컬 검수 서버에 연결할 수 없습니다.`,`에이전트에게 검수 링크를 다시 요청해 주세요. 서버가 쉬고 있으면 링크를 만들 때 다시 켜집니다.`,503,!0)}},o=async(e,{surface:t=!1}={})=>{let n=await(await a(`/api/reviews/${encodeURIComponent(e)}${t?`?surface=1`:``}`)).json();return{run:n.run,job:n.job,source_text:n.source_text,draft_text:n.draft_text,attachments:n.attachments??[],surface:n.source_surface??null}},s=async(e,t)=>(await a(`/api/reviews/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}`)).blob(),c=async(e,t)=>{if(t.length===0)return[];let n=await a(`/api/review-feedback`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({review_id:e,items:t})}),i;try{i=await n.json()}catch{i=void 0}let o=i?.results;if(!Array.isArray(o)||o.length!==t.length)throw r(`invalid_server_response`,`로컬 검수 서버 응답이 올바르지 않습니다.`,`서버를 다시 실행해 주세요.`,502);return o},l=async(e,t=!1)=>{await a(`/api/review-finalize`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({review_id:e,force:t})})},u=120,d={unspecified:`?`,deletion:`삭제하기`,awkward_expression:`어색한 표현`,terminology:`용어`,omission:`누락`,addition:`추가`,meaning_shift:`의미 변화`,register:`문체`},f=[`unspecified`,`deletion`,`terminology`,`meaning_shift`,`awkward_expression`,`omission`,`addition`,`register`],p=[`run_only`,`personal`],m=new Set([`terminology`,`meaning_shift`]),h=[`deletion`],g=new Set([`terminology`]),_
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),t.credentials=e.crossOrigin===`use-credentials`?`include`:e.crossOrigin===`anonymous`?`omit`:`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=()=>{if(typeof crypto<`u`&&typeof crypto.randomUUID==`function`)return crypto.randomUUID();let e=new Uint8Array(16);crypto.getRandomValues(e),e[6]=(e[6]??0)&15|64,e[8]=(e[8]??0)&63|128;let t=Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``);return`${t.slice(0,8)}-${t.slice(8,12)}-${t.slice(12,16)}-${t.slice(16,20)}-${t.slice(20)}`},t=(e,t)=>{let n=e.body;return[n?.message??(e instanceof Error?e.message:t),n?.next_action].filter(Boolean).join(` `)},n=(e,t,n,r=!1)=>({code:e,message:t,next_action:n,retryable:r}),r=(e,t,r,i,a=!1)=>Object.assign(Error(t),{status:i,body:n(e,t,r,a)}),i=async e=>{let t;try{t=await e.json()}catch{t=void 0}throw Object.assign(Error(t?.message??`로컬 검수 서버 요청에 실패했습니다.`),{status:e.status,body:t})},a=async(e,t)=>{try{let n=await fetch(e,{cache:`no-store`,...t});return n.ok||await i(n),n}catch(e){throw e.body?e:r(`local_server_unavailable`,`로컬 검수 서버에 연결할 수 없습니다.`,`에이전트에게 검수 링크를 다시 요청해 주세요. 서버가 쉬고 있으면 링크를 만들 때 다시 켜집니다.`,503,!0)}},o=async(e,{surface:t=!1}={})=>{let n=await(await a(`/api/reviews/${encodeURIComponent(e)}${t?`?surface=1`:``}`)).json();return{run:n.run,job:n.job,source_text:n.source_text,draft_text:n.draft_text,attachments:n.attachments??[],surface:n.source_surface??null}},s=async(e,t)=>(await a(`/api/reviews/${encodeURIComponent(e)}/attachments/${encodeURIComponent(t)}`)).blob(),c=async(e,t)=>{if(t.length===0)return[];let n=await a(`/api/review-feedback`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({review_id:e,items:t})}),i;try{i=await n.json()}catch{i=void 0}let o=i?.results;if(!Array.isArray(o)||o.length!==t.length)throw r(`invalid_server_response`,`로컬 검수 서버 응답이 올바르지 않습니다.`,`서버를 다시 실행해 주세요.`,502);return o},l=async(e,t=!1)=>{await a(`/api/review-finalize`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({review_id:e,force:t})})},u=120,d={unspecified:`?`,deletion:`삭제하기`,awkward_expression:`어색한 표현`,terminology:`용어`,omission:`누락`,addition:`추가`,meaning_shift:`의미 변화`,register:`문체`},f=[`unspecified`,`deletion`,`terminology`,`meaning_shift`,`awkward_expression`,`omission`,`addition`,`register`],p=[`run_only`,`personal`],m=new Set([`terminology`,`meaning_shift`]),h=[`deletion`],g=new Set([`unspecified`,`awkward_expression`,`terminology`,`register`]),_=`개인 영구 규칙 후보로 Gist에 남깁니다.`,v=e=>{if(e.onceOnly)return{scope:`run_only`};if(!g.has(e.category))return{scope:`run_only`,blocked:`category`};if(!e.correction?.normalize(`NFC`).trim())return{scope:`run_only`,blocked:`no_correction`};let t=(e.term||e.selectedText).normalize(`NFC`);return Array.from(t).length>u?{scope:`run_only`,blocked:`no_term`}:{scope:`personal`}},y=e=>e===`category`?`이 유형은 이번 문서에만 적용됩니다.`:e===`no_correction`?`수정문을 적으면 개인 영구 규칙 후보로 남길 수 있습니다.`:`문장이 길어 이번 문서에만 적용됩니다. 단어를 지정하면 영구 규칙 후보가 됩니다.`;export{_ as a,y as c,l as d,e as f,p as i,o as l,c as m,m as n,d as o,t as p,f as r,v as s,h as t,s as u};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{a as e,c as t,l as n,m as r,n as i,o as a,p as o,r as s,s as c,t as l}from"./feedback-contract-_524rCEj.js";var u=new URLSearchParams(location.search),d=new URLSearchParams(location.hash.startsWith(`#`)?location.hash.slice(1):location.hash).get(`review`),f=!!d,p=null,ee=u.get(`target`),m=document.getElementById(`source`),h=null,g=null,_=null,te=``,v=document.getElementById(`reviewMode`),y=document.getElementById(`basket`),b=document.getElementById(`items`),ne=document.getElementById(`count`),x=document.getElementById(`sheet`),re=document.getElementById(`selection`),S=document.getElementById(`correction`),ie=document.getElementById(`status`),ae=document.getElementById(`barMode`),C=document.getElementById(`add`),w=document.getElementById(`copyFeedback`),T=document.getElementById(`remember`),E=`gyojeong:${d||`local`}:pending`,D=u.get(`selectors`)||`h1,h2,h3,h4,h5,h6,p,li,td,th,blockquote,figcaption,[data-review-text]`,O=document.getElementById(`surface`),k=document.getElementById(`categories`),A=document.getElementById(`scope`),oe=document.getElementById(`termLabel`),j=document.getElementById(`term`),se=document.getElementById(`correctionLabel`),ce=document.getElementById(`rememberLabel`),le=document.getElementById(`scopeNote`),M=!0,N=null,P=[],F=[],I=!f,ue=!1,L=s[0],R=`sentence`;f&&(w.textContent=`피드백 전송`);function z(){if(typeof crypto.randomUUID==`function`)return crypto.randomUUID();let e=new Uint8Array(16);return crypto.getRandomValues(e),`feedback-${Date.now().toString(36)}-${Array.from(e,e=>e.toString(16).padStart(2,`0`)).join(``)}`}function B(){try{return JSON.parse(localStorage.getItem(E)||`[]`)}catch{return[]}}function V(){try{return localStorage.setItem(E,JSON.stringify(F)),!0}catch{return!1}}function H(e){ie.textContent=e,clearTimeout(H.timer),H.timer=setTimeout(()=>{ie.textContent=`문장을 눌러 피드백을 남기세요.`},2200)}function U(){return l.includes(L)}function W(){if(!N)return null;if(R===`paragraph`){let e=Array.from(N.paragraphText),t=0;for(;t<e.length&&/\s/u.test(e[t]);)t+=1;let n=0;for(;n<e.length-t&&/\s/u.test(e[e.length-1-n]);)n+=1;return{start:N.paragraphStart+t,end:N.paragraphEnd-n,text:e.slice(t,e.length-n).join(``)}}return{start:N.start,end:N.end,text:N.text}}function G(){for(let e of A.children){let t=e.dataset.mode===R;e.classList.toggle(`active`,t),e.setAttribute(`aria-checked`,String(t))}let e=R===`word`;oe.hidden=!e,j.hidden=!e,q();let t=W();t&&(re.textContent=t.text.trim())}function K(){for(let e of k.children)e.classList.toggle(`active`,e.dataset.category===L),e.setAttribute(`aria-checked`,String(e.dataset.category===L));let e=U();se.hidden=e,S.hidden=e,e&&(S.value=``),se.textContent=i.has(L)?`수정문 · 필수`:`수정문 · 선택 사항`,q()}function de(){return c({category:L,onceOnly:T.checked,term:R===`word`?j.value.normalize(`NFC`).trim():void 0,selectedText:W()?.text??``,correction:S.value})}function q(){let n=de();ce.hidden=n.blocked===`category`,le.textContent=n.blocked?t(n.blocked):n.scope===`personal`?e:`이번 문서에만 적용됩니다.`}function fe(){k.setAttribute(`role`,`radiogroup`),k.setAttribute(`aria-label`,`문제 유형`),k.replaceChildren(),s.includes(L)||(L=s[0]);for(let e of s){let t=document.createElement(`button`);t.type=`button`,t.dataset.category=e,t.setAttribute(`role`,`radio`),t.textContent=a[e]||e,t.addEventListener(`click`,()=>{L=e,K()}),k.append(t)}K()}function J(e){ue||(ue=!0,Se(),H(`원본에서 문장을 연결할 수 없어 검수용 보기로 전환합니다. (${e})`),f&&setTimeout(()=>{location.href=`/#review=${encodeURIComponent(d)}`},700))}async function Y(){let e=await n(d,{surface:!0});return p=e.job.id,e}async function pe(){if(!f)return!0;if((await Y()).draft_text!==window.__feedbackDraftText)throw Error(`text-mismatch`);return!0}async function me(e){let t=ee,n=null;if(!t&&f){let r=e??await Y();t=r.surface?.target_url??null,n=r.surface?.selectors??null}if(!t)throw Error(`missing-target`);let r=new URL(t,location.href);if(r.origin!==location.origin)throw Error(`cross-origin-target`);return n&&!u.get(`selectors`)&&(D=n),r.href}async function he(){let e=null;if(f&&!ee)try{e=await Y()}catch(e){J(e.message||`review-unavailable`);return}if(e?.surface?.snapshot){ge(e);return}try{h=await me(e)}catch(e){J(e.message||`surface-unavailable`);return}E=`gyojeong:${d||`local`}:${h}`,F=B(),Q(),m.src=h}function ge(e){g=e.surface.snapshot,te=e.draft_text,h=e.surface.target_url,E=`gyojeong:${d||`local`}:snapshot:${h}`,F=B(),Q(),m.hidden=!0,O.hidden=!1,I=!0,ve(),H(`원본을 캡처한 화면에 검수 레이어를 연결했습니다.`)}function _e(e,t){return new Promise((n,r)=>{t.addEventListener(`load`,()=>n(),{once:!0}),t.addEventListener(`error`,()=>r(Error(`tile-unavailable`)),{once:!0}),t.src=`data:${e.media_type};base64,${e.content_base64}`})}function ve(){O.replaceChildren();let e=O.clientWidth/g.page_width,t=document.createElement(`div`);t.className=`surface-page`,t.style.width=`${g.page_width*e}px`,t.style.height=`${g.page_height*e}px`,O.append(t),_||=g.tiles.map(e=>{let t=document.createElement(`img`);return t.alt=``,t.className=`surface-tile`,_e(e,t).catch(()=>J(`tile-unavailable`)),t});for(let[n,r]of g.tiles.entries()){let i=_[n];i.style.top=`${r.top*e}px`,i.style.width=`${g.page_width*e}px`,i.style.height=`${r.height*e}px`,t.append(i)}if(!M)return;let n=[...te];for(let[r,i]of g.units.entries()){let a=`unit-${r}`,o=n.slice(i.start,i.end).join(``),s=F.some(e=>e.start<=i.start&&e.end>=i.end);for(let[r,[c,l,u,d]]of i.rects.entries()){let f=document.createElement(`button`);f.type=`button`,f.className=`surface-hit`,f.dataset.group=a,f.dataset.pinned=String(s),r===0?f.setAttribute(`aria-label`,`${o} 피드백 남기기`):(f.tabIndex=-1,f.setAttribute(`aria-hidden`,`true`)),f.style.left=`${c*e}px`,f.style.top=`${l*e}px`,f.style.width=`${u*e}px`,f.style.height=`${d*e}px`,f.addEventListener(`click`,e=>{e.preventDefault(),we({text:o,start:i.start,end:i.end,paragraphText:i.block?n.slice(i.block[0],i.block[1]).join(``):o,paragraphStart:i.block?i.block[0]:i.start,paragraphEnd:i.block?i.block[1]:i.end})}),t.append(f)}}}function ye(e){let t=new Uint32Array(e.length+1),n=0;for(let r=0;r<e.length;r+=1){t[r]=n;let i=e.charCodeAt(r),a=e.charCodeAt(r+1);i>=55296&&i<=56319&&a>=56320&&a<=57343&&(t[r+1]=n+1,r+=1),n+=1}return t[e.length]=n,t}function X(e){let t=[],n=e.ownerDocument.createTreeWalker(e,NodeFilter.SHOW_TEXT,{acceptNode(t){return!t.data.trim()||t.parentElement.closest(`.notes,[aria-hidden="true"],script,style`)||t.parentElement.closest(D)!==e?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}});for(;n.nextNode();)t.push(n.currentNode);return t}function be(e,t){let n=0;for(let r of e){let e=n+r.data.length;if(t<=e)return{node:r,offset:Math.max(0,t-n)};n=e}let r=e.at(-1);return r?{node:r,offset:r.data.length}:null}function xe(e){let t=X(e);if(!t.length)return[];let n=t.map(e=>e.data).join(``);return[...new Intl.Segmenter(`ko`,{granularity:`sentence`}).segment(n)].flatMap(n=>{let r=n.segment,i=r.search(/\S/);if(i<0)return[];let a=r.length-r.trimEnd().length,o=be(t,n.index+i),s=be(t,n.index+r.length-a);if(!o||!s)return[];let c=e.ownerDocument.createRange();try{c.setStart(o.node,o.offset),c.setEnd(s.node,s.offset)}catch{return[]}return[{text:r.trim(),range:c,start:n.index+i,end:n.index+r.length-a}]})}function Se(){for(let e of P)e.remove();P=[]}function Z(){Se();let e;try{e=m.contentDocument}catch{J(`cross-origin`);return}if(!e?.body||!M)return;let t=e.createElement(`div`);t.id=`feedback-overlay-root`,Object.assign(t.style,{position:`absolute`,inset:`0`,zIndex:`2147483646`,pointerEvents:`none`}),e.body.append(t),P.push(t);let n=[...e.querySelectorAll(D)].filter(e=>!e.closest(`.notes,.controls,.note-panel,#feedback-overlay-root,[aria-hidden="true"]`)&&X(e).map(e=>e.data).join(``).trim().length>1),r=n.map(e=>X(e).map(e=>e.data).join(``));window.__feedbackDraftText=r.join(`
|
|
2
|
+
|
|
3
|
+
`);let i=0,a=0;for(let[o,s]of n.entries()){let c=r[o],l=a,u=ye(c);for(let n of xe(s)){let r=`sentence-${i++}`,a=l+u[n.start],o=l+u[n.end],s=[...n.range.getClientRects()].filter(e=>e.width>2&&e.height>2);for(let[i,d]of s.entries()){let s=e.createElement(`button`);s.type=`button`,s.disabled=!I,s.dataset.group=r,i===0?s.setAttribute(`aria-label`,`${n.text} 피드백 남기기`):(s.tabIndex=-1,s.setAttribute(`aria-hidden`,`true`));let f=F.some(e=>e.start<=a&&e.end>=o);s.dataset.pinned=String(f),Object.assign(s.style,{position:`absolute`,left:`${d.left+m.contentWindow.scrollX}px`,top:`${d.top+m.contentWindow.scrollY}px`,width:`${d.width}px`,height:`${d.height}px`,margin:`0`,padding:`0`,border:`1px solid transparent`,borderRadius:`2px`,background:f?`rgba(0,0,0,.18)`:`rgba(0,0,0,.01)`,borderColor:f?`rgba(0,0,0,.78)`:`transparent`,cursor:I?`pointer`:`progress`,pointerEvents:`auto`}),s.addEventListener(`mouseenter`,()=>Ce(e,r,!0)),s.addEventListener(`mouseleave`,()=>Ce(e,r,!1)),s.addEventListener(`click`,e=>{e.preventDefault(),e.stopPropagation(),we({text:n.text,start:a,end:o,paragraphText:c,paragraphStart:l,paragraphEnd:l+u[c.length]})}),t.append(s)}}a+=Array.from(c).length+(o<n.length-1?2:0)}}function Ce(e,t,n){for(let r of e.querySelectorAll(`[data-group="${t}"]`)){let e=r.dataset.pinned===`true`;r.style.borderColor=n||e?`rgba(0,0,0,.78)`:`transparent`,r.style.background=n?`rgba(0,0,0,.10)`:e?`rgba(0,0,0,.18)`:`rgba(0,0,0,.01)`}}function we(e){N=e,R=`sentence`,S.value=``,j.value=``,T.checked=!1,G(),K(),x.classList.add(`open`),C.hidden=!1,Q()}function Q(){let e=F.filter(e=>!e.submitted);if(ne.textContent=String(F.length),w.disabled=e.length===0&&!x.classList.contains(`open`),b.replaceChildren(),!F.length){let e=document.createElement(`p`);e.className=`item`,e.textContent=`아직 담긴 피드백이 없습니다.`,b.append(e);return}F.forEach((e,t)=>{let n=document.createElement(`div`);n.className=`item`;let r=document.createElement(`b`);r.textContent=`${a[e.category]||e.category}${e.term?` · ${e.term}`:``}${e.submitted?` · 전송됨`:``}`;let i=document.createElement(`p`);i.textContent=e.text;let o=document.createElement(`p`);o.textContent=e.correction||`수정 제안 없음`;let s=document.createElement(`button`);s.type=`button`,s.textContent=`제거`,s.addEventListener(`click`,()=>{F.splice(t,1);let e=V();Q(),$(),e||H(`검수함 변경은 현재 탭에서만 유지됩니다.`)}),n.append(r,i,o,s),b.append(n)})}m.addEventListener(`load`,async()=>{try{if(Z(),!m.contentDocument?.body){J(`iframe-access`);return}await pe(),I=!0,Z()}catch(e){J(e.message||`validation-failed`);return}H(`원본 iframe에 검수 레이어를 연결했습니다.`)});var $=()=>g?ve():Z(),Te;addEventListener(`resize`,()=>{clearTimeout(Te),Te=setTimeout($,120)}),v.addEventListener(`click`,()=>{M=!M,v.textContent=M?`검수 모드`:`읽기 모드`,v.setAttribute(`aria-pressed`,String(M)),ae.textContent=M?`검수 중`:`읽기 중`,M||(x.classList.remove(`open`),C.hidden=!0),$()}),T.addEventListener(`change`,q),j.addEventListener(`input`,q),S.addEventListener(`input`,q),A.addEventListener(`click`,e=>{let t=e.target.closest(`button[data-mode]`);!t||!N||(R=t.dataset.mode,G())}),document.getElementById(`showBasket`).addEventListener(`click`,()=>{y.hidden=!y.hidden,Q()}),document.getElementById(`closeBasket`).addEventListener(`click`,()=>{y.hidden=!0}),document.getElementById(`cancel`).addEventListener(`click`,()=>{x.classList.remove(`open`),C.hidden=!0,Q()});var Ee=()=>{if(!N)return H(`선택한 문장을 다시 눌러 주세요.`),!1;let e=U()?``:S.value.normalize(`NFC`).trim();if(i.has(L)&&!e)return H(`이 분류에는 수정문이 필요합니다.`),S.focus(),!1;let t=W(),n=R===`word`?j.value.normalize(`NFC`).trim():``;if(R===`word`){if(!n)return H(`어떤 표현에 대한 지적인지 적어 주세요.`),j.focus(),!1;if(!t.text.normalize(`NFC`).includes(n))return H(`대상 표현이 선택한 문장 안에 없습니다.`),j.focus(),!1}F.push({...N,id:z(),category:L,mode:R,start:t.start,end:t.end,text:t.text,...n?{term:n}:{},scope:de().scope,correction:e,created_at:new Date().toISOString(),submitted:!1});let r=V();return x.classList.remove(`open`),C.hidden=!0,Q(),$(),H(r?`검수함에 추가했습니다.`:`검수함에 추가했습니다. 현재 탭에서만 유지됩니다.`),!0};document.getElementById(`add`).addEventListener(`click`,()=>{Ee()}),document.getElementById(`copyFeedback`).addEventListener(`click`,async()=>{if(x.classList.contains(`open`)&&!Ee())return;if(!f){await navigator.clipboard.writeText(JSON.stringify({source:m.src,feedback:F},null,2)),H(`피드백 JSON을 복사했습니다.`);return}if(!p){H(`전송 실패: 작업 정보를 아직 불러오지 못했습니다.`);return}let e=F.filter(e=>!e.submitted);if(!e.length){H(`먼저 문장이나 문단을 선택하고 검수함에 추가해 주세요.`);return}w.disabled=!0,H(`${e.length}개 피드백을 전송하고 있습니다.`);try{let t=await r(d,e.map(e=>({input:{job_id:p,category:e.category,scope:e.scope||`run_only`,selection:{start:e.start,end:e.end,selected_text:e.text},...e.term?{term:e.term}:{},...e.correction?{correction:e.correction}:{}},idempotencyKey:e.id||z()})));e.forEach((e,n)=>{e.submitted=!0,e.feedback_id=t[n].feedback_id});let n=V();Q(),$(),H(n?`피드백을 전송했습니다.`:`피드백을 전송했습니다. 브라우저 검수함 상태는 저장하지 못했습니다.`)}catch(e){Q(),H(`전송 실패: ${o(e,`다시 시도해 주세요.`)}`)}}),fe(),Q(),he();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--ink:#000;--paper:#fff;--muted:#b3b3b3;--rule:#d4d4d4;--mono:"SF Mono", "Menlo", "D2Coding", "Consolas", monospace;--serif:"Apple SD Gothic Neo", "Noto Serif CJK KR", "Nanum Myeongjo", "AppleMyungjo", serif;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;font-family:var(--mono)}*{box-sizing:border-box}html,body{background:#dfe4e8;width:100%;height:100%;margin:0;overflow:hidden}button,textarea{font:inherit}#source{background:#fff;border:0;width:100%;height:100%}#surface{background:#fff;width:100%;height:100%;overflow:auto}.surface-page{margin:0 auto;position:relative}.surface-tile{display:block;position:absolute;left:0}.surface-hit{cursor:pointer;background:#00000003;border:1px solid #0000;border-radius:2px;margin:0;padding:0;position:absolute}.surface-hit:hover,.surface-hit:focus-visible{background:#0000001a;border-color:#000000c7}.surface-hit[data-pinned=true]{background:#0000002e;border-color:#000000c7}.mode-toggle-floating{z-index:25;border:1px solid var(--ink);background:var(--ink);min-height:30px;color:var(--paper);cursor:pointer;letter-spacing:.08em;padding:0 10px;font-size:.68rem;position:fixed;top:18px;right:18px}.mode-toggle-floating[aria-pressed=false]{background:var(--paper);color:var(--ink)}.sheet{z-index:19;left:0;right:0;bottom:calc(54px + env(safe-area-inset-bottom,0px));background:var(--ink);max-height:62dvh;color:var(--paper);border-top:1px solid #4d4d4d;padding:22px max(16px,50vw - 20.5rem) 26px;transition:transform .16s ease-out;position:fixed;overflow-y:auto;transform:translateY(calc(100% + 70px))}.sheet.open{transform:translateY(0)}.sheet-head{letter-spacing:.16em;text-transform:uppercase;justify-content:space-between;align-items:baseline;margin-bottom:12px;font-size:.68rem;display:flex}.sheet-head button{min-width:30px;min-height:30px;color:var(--paper);cursor:pointer;background:0 0;border:0;font-size:1rem}.selection{border-left:2px solid var(--paper);max-height:7.2em;color:var(--paper);font-family:var(--serif);white-space:pre-line;margin:0 0 20px;padding-left:12px;font-size:1rem;line-height:1.8;overflow-y:auto}.label{color:var(--muted);letter-spacing:.18em;text-transform:uppercase;margin:0 0 8px;font-size:.62rem}.scope,.categories{flex-wrap:wrap;gap:6px;margin-bottom:20px;display:flex}.scope button,.categories button{cursor:pointer;min-height:30px;color:var(--paper);background:0 0;border:1px solid #6b6b6b;padding:0 11px;font-size:.72rem}.scope button.active,.categories button.active{border-color:var(--paper);background:var(--paper);color:var(--ink)}.scope button:disabled{opacity:.4;cursor:default}#term{width:100%;color:var(--paper);font-family:var(--serif);background:0 0;border:1px solid #6b6b6b;margin-bottom:20px;padding:8px 12px;font-size:.98rem}textarea{resize:vertical;width:100%;min-height:76px;color:var(--paper);font-family:var(--serif);background:0 0;border:1px solid #6b6b6b;border-radius:0;padding:10px 12px;font-size:.98rem;line-height:1.5}textarea::placeholder{color:#767676}.remember{align-items:center;gap:9px;min-height:42px;font-size:.72rem;display:flex}[hidden]{display:none!important}.remember input{width:14px;height:14px;accent-color:var(--paper)}.scope-note{color:var(--muted);margin:0 0 4px;font-size:.68rem}.review-bar{z-index:20;min-height:54px;padding:12px max(16px, calc((100vw - 41rem) / 2)) calc(12px + env(safe-area-inset-bottom,0px));background:var(--ink);color:var(--paper);letter-spacing:.08em;align-items:center;gap:16px;font-size:.72rem;display:flex;position:fixed;bottom:0;left:0;right:0}.bar-mode{text-transform:uppercase}.bar-count{color:var(--muted);cursor:pointer;font:inherit;letter-spacing:inherit;background:0 0;border:0;margin-right:auto;padding:0}.bar-add,.bar-send{cursor:pointer;letter-spacing:.08em;white-space:nowrap;min-height:30px;padding:0 14px;font-size:.72rem}.bar-add{color:var(--paper);background:0 0;border:1px solid #6b6b6b}.bar-send{border:1px solid var(--paper);background:var(--paper);color:var(--ink)}.bar-send:disabled{color:#6b6b6b;cursor:default;background:0 0;border-color:#4d4d4d}.bar-status{color:var(--muted);margin:0}.basket{z-index:18;right:18px;bottom:calc(70px + env(safe-area-inset-bottom,0px));border:1px solid var(--ink);background:var(--paper);width:min(360px,100vw - 36px);max-height:min(48vh,460px);color:var(--ink);padding:12px;position:fixed;overflow:auto;box-shadow:0 10px 32px #00000029}.basket[hidden]{display:none}.basket header{justify-content:space-between;align-items:center;gap:10px;margin-bottom:8px;display:flex}.basket h2{margin:0;font-size:15px}.basket button{cursor:pointer;border:1px solid var(--ink);background:var(--paper);color:var(--ink);font:.72rem var(--mono);padding:7px 9px}.item{border-top:1px solid #e0e5e8;padding:9px 2px;font-size:13px;line-height:1.45}.item:first-of-type{border-top:0}.item b{color:var(--ink)}.item p{color:#52616c;margin:4px 0 0}@media (width<=700px){.mode-toggle-floating{top:8px;right:8px}.review-bar{padding:10px 12px calc(10px + env(safe-area-inset-bottom,0px));gap:9px}.review-bar .bar-status{display:none}.bar-add,.bar-send{min-height:44px;padding:0 12px}.sheet{bottom:calc(64px + env(safe-area-inset-bottom,0px));max-height:70dvh;padding:18px 16px 22px}.basket{right:8px;bottom:calc(76px + env(safe-area-inset-bottom,0px));width:calc(100vw - 16px);max-height:38vh}}@media (pointer:coarse){.mode-toggle-floating,.scope button,.categories button,.bar-add,.bar-send,.basket button,.remember{min-height:44px}.sheet-head button{min-width:44px;min-height:44px}.remember input{width:20px;height:20px}}@media (prefers-reduced-motion:reduce){.sheet{transition:none}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--ink:#000;--paper:#fff;--muted:#6b6b6b;--rule:#d4d4d4;--wash:#f2f2f2;--serif:"Apple SD Gothic Neo", "Noto Serif CJK KR", "Nanum Myeongjo", "AppleMyungjo", serif;--mono:"SF Mono", "Menlo", "D2Coding", "Consolas", monospace;--column:41rem;--bar-height:54px;--safe-bottom:env(safe-area-inset-bottom,0px);--sheet-bottom:calc(var(--bar-height) + var(--safe-bottom));--sheet-max-height:62dvh;color:var(--ink);background:var(--paper);font-family:var(--mono);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}body{background:var(--paper);min-width:320px;min-height:100dvh;margin:0}button,select,textarea,input{font:inherit;color:inherit}button,select,textarea,[tabindex]{outline-offset:2px;outline:2px solid #0000}button:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline-color:var(--ink)}.desk{width:min(var(--column) + 48px, 100vw - 32px);margin:0 auto;padding:0 0 40dvh}.topline{z-index:5;border-bottom:1px solid var(--ink);background:var(--paper);align-items:baseline;gap:14px;padding:18px 0 10px;display:flex;position:sticky;top:0}.route{letter-spacing:.06em;text-transform:lowercase;margin:0 auto 0 0;font-size:.7rem}.mode-toggle{border:1px solid var(--ink);background:var(--paper);cursor:pointer;letter-spacing:.08em;min-height:30px;padding:0 10px;font-size:.68rem}.mode-toggle[aria-pressed=true]{background:var(--ink);color:var(--paper)}.doc{max-width:var(--column);font-family:var(--serif);word-break:keep-all;overflow-wrap:anywhere;margin:34px auto 0;font-size:1.08rem;line-height:2.05}.doc-block{margin:0 0 1.35em}.doc-block.doc-h1,.doc-block.doc-h2,.doc-block.doc-h3,.doc-block.doc-h4{font-family:var(--mono);letter-spacing:.2em;text-transform:uppercase;margin:2.4em 0 .9em;font-size:.72rem;font-weight:400;line-height:1.5}.doc-block.doc-h1:before,.doc-block.doc-h2:before,.doc-block.doc-h3:before{content:"";border-top:1px solid var(--ink);width:100%;margin-bottom:.9em;display:block}.doc-block.doc-pre{font-family:var(--mono);font-size:.82rem;line-height:1.7}.doc-block.doc-pre pre{border-left:2px solid var(--ink);background:var(--wash);margin:0;padding:14px 16px;overflow-x:auto}.doc code{font-family:var(--mono);font-size:.86em}.doc a{color:inherit;text-underline-offset:3px;text-decoration-thickness:1px}.doc table{border-collapse:collapse;font-size:.9rem;display:block;overflow-x:auto}.doc :is(th,td){border:1px solid var(--rule);padding:6px 10px}.doc blockquote{border-left:2px solid var(--ink);margin:0;padding-left:14px}.doc img{max-width:100%;height:auto}.preview-attachment{max-width:min(100%,760px);margin:18px 0;display:block}.preview-attachment img,.preview-inline-attachment{border:1px solid var(--rule);object-fit:contain;max-width:100%;max-height:70vh;display:block}.preview-inline-attachment{margin:18px 0 6px}.preview-inline-attachment[hidden]{display:none}.preview-attachment figcaption{color:var(--muted);font-family:var(--mono);margin-top:6px;font-size:.76rem}.preview-image-note{color:var(--muted);font-family:var(--mono);font-size:.76rem;display:inline-block}.unit{cursor:pointer;transition:background .12s linear,color .12s linear}.doc:not(.is-reading) .unit:hover,.doc:not(.is-reading) .unit:focus-visible,.doc:not(.is-reading) .unit.is-hover-group{text-underline-offset:5px;outline:none;text-decoration:underline;text-decoration-thickness:1px}.doc.is-reading .unit{cursor:text}.unit.is-pinned{background:var(--ink);color:var(--paper);padding:0 1px;text-decoration:none}.unit.is-pinned :is(code,a,strong,em){color:inherit}.pin{font-family:var(--mono);vertical-align:super;margin-left:3px;font-size:.62rem;font-weight:400}.pin-scope{gap:8px;margin:0 0 12px;display:flex}.pin-scope .chip:disabled{opacity:.4;cursor:default}.pin-term{box-sizing:border-box;border:1px solid var(--rule);width:100%;min-height:44px;font-family:var(--mono);margin:0 0 12px;padding:10px 12px;font-size:.92rem}.pin-siblings{border-top:1px solid var(--rule);flex-wrap:wrap;gap:8px;margin:12px 0 0;padding-top:12px;display:flex}.pin-sibling{border:1px solid var(--rule);min-height:44px;font-family:var(--mono);color:var(--muted);cursor:pointer;background:0 0;padding:0 12px;font-size:.78rem}.pin-siblings[hidden]{display:none}.source-panel{max-width:var(--column);border:1px solid var(--rule);margin:24px auto 0}.source-panel>summary{box-sizing:border-box;min-height:44px;font-family:var(--mono);color:var(--muted);cursor:pointer;align-items:center;padding:12px 14px;font-size:.78rem;display:flex}.source-text{border-top:1px solid var(--rule);font-family:var(--serif);color:var(--muted);white-space:pre-wrap;word-break:keep-all;overflow-wrap:anywhere;padding:14px;font-size:.98rem;line-height:1.85}.review-bar{z-index:20;padding:12px max(16px, calc((100vw - var(--column)) / 2));padding-bottom:calc(12px + var(--safe-bottom));background:var(--ink);color:var(--paper);letter-spacing:.08em;align-items:center;gap:16px;font-size:.72rem;display:flex;position:fixed;bottom:0;left:0;right:0}.bar-mode{text-transform:uppercase}.bar-count{color:#b3b3b3;margin-right:auto}.bar-add,.bar-send,.bar-finish{cursor:pointer;letter-spacing:.08em;white-space:nowrap;min-height:30px;padding:0 14px;font-size:.72rem}.bar-add,.bar-finish{color:var(--paper);background:0 0;border:1px solid #6b6b6b}.bar-send{border:1px solid var(--paper);background:var(--paper);color:var(--ink)}.bar-add:disabled,.bar-send:disabled,.bar-finish:disabled{color:#6b6b6b;cursor:default;background:0 0;border-color:#4d4d4d}.review-bar .status{color:#b3b3b3;margin:0}.pin-card{left:0;right:0;bottom:var(--sheet-bottom);z-index:19;max-height:var(--sheet-max-height);padding:22px max(16px, calc((100vw - var(--column)) / 2)) 26px;background:var(--ink);color:var(--paper);border-top:1px solid #4d4d4d;transition:bottom .16s ease-out,max-height .16s ease-out;animation:.16s ease-out sheet-up;position:fixed;overflow-y:auto}@keyframes sheet-up{0%{opacity:0;transform:translateY(12px)}}.pin-card-head{letter-spacing:.16em;text-transform:uppercase;justify-content:space-between;align-items:baseline;margin-bottom:12px;font-size:.68rem;display:flex}.pin-card-close{min-width:30px;min-height:30px;color:var(--paper);cursor:pointer;background:0 0;border:0;font-size:1rem}.pin-quote{border-left:2px solid var(--paper);font-family:var(--serif);max-height:7.2em;margin:0 0 20px;padding-left:12px;font-size:1rem;line-height:1.8;overflow-y:auto}.label{color:#b3b3b3;letter-spacing:.18em;text-transform:uppercase;margin:0 0 8px;font-size:.62rem}.chips{flex-wrap:wrap;gap:6px;margin-bottom:20px;display:flex}.chip{min-height:30px;color:var(--paper);cursor:pointer;background:0 0;border:1px solid #6b6b6b;padding:0 11px;font-size:.72rem}.chip.is-on{border-color:var(--paper);background:var(--paper);color:var(--ink)}.pin-correction{width:100%;min-height:76px;color:var(--paper);font-family:var(--serif);resize:vertical;background:0 0;border:1px solid #6b6b6b;border-radius:0;padding:10px 12px;font-size:.98rem}.pin-correction::placeholder{color:#
|
|
1
|
+
:root{--ink:#000;--paper:#fff;--muted:#6b6b6b;--rule:#d4d4d4;--wash:#f2f2f2;--serif:"Apple SD Gothic Neo", "Noto Serif CJK KR", "Nanum Myeongjo", "AppleMyungjo", serif;--mono:"SF Mono", "Menlo", "D2Coding", "Consolas", monospace;--column:41rem;--bar-height:54px;--safe-bottom:env(safe-area-inset-bottom,0px);--sheet-bottom:calc(var(--bar-height) + var(--safe-bottom));--sheet-max-height:62dvh;color:var(--ink);background:var(--paper);font-family:var(--mono);font-synthesis:none;text-rendering:optimizelegibility}*{box-sizing:border-box}body{background:var(--paper);min-width:320px;min-height:100dvh;margin:0}button,select,textarea,input{font:inherit;color:inherit}button,select,textarea,[tabindex]{outline-offset:2px;outline:2px solid #0000}button:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline-color:var(--ink)}.desk{width:min(var(--column) + 48px, 100vw - 32px);margin:0 auto;padding:0 0 40dvh}.topline{z-index:5;border-bottom:1px solid var(--ink);background:var(--paper);align-items:baseline;gap:14px;padding:18px 0 10px;display:flex;position:sticky;top:0}.route{letter-spacing:.06em;text-transform:lowercase;margin:0 auto 0 0;font-size:.7rem}.mode-toggle{border:1px solid var(--ink);background:var(--paper);cursor:pointer;letter-spacing:.08em;min-height:30px;padding:0 10px;font-size:.68rem}.mode-toggle[aria-pressed=true]{background:var(--ink);color:var(--paper)}.doc{max-width:var(--column);font-family:var(--serif);word-break:keep-all;overflow-wrap:anywhere;margin:34px auto 0;font-size:1.08rem;line-height:2.05}.doc-block{margin:0 0 1.35em}.doc-block.doc-h1,.doc-block.doc-h2,.doc-block.doc-h3,.doc-block.doc-h4{font-family:var(--mono);letter-spacing:.2em;text-transform:uppercase;margin:2.4em 0 .9em;font-size:.72rem;font-weight:400;line-height:1.5}.doc-block.doc-h1:before,.doc-block.doc-h2:before,.doc-block.doc-h3:before{content:"";border-top:1px solid var(--ink);width:100%;margin-bottom:.9em;display:block}.doc-block.doc-pre{font-family:var(--mono);font-size:.82rem;line-height:1.7}.doc-block.doc-pre pre{border-left:2px solid var(--ink);background:var(--wash);margin:0;padding:14px 16px;overflow-x:auto}.doc code{font-family:var(--mono);font-size:.86em}.doc a{color:inherit;text-underline-offset:3px;text-decoration-thickness:1px}.doc table{border-collapse:collapse;font-size:.9rem;display:block;overflow-x:auto}.doc :is(th,td){border:1px solid var(--rule);padding:6px 10px}.doc blockquote{border-left:2px solid var(--ink);margin:0;padding-left:14px}.doc img{max-width:100%;height:auto}.preview-attachment{max-width:min(100%,760px);margin:18px 0;display:block}.preview-attachment img,.preview-inline-attachment{border:1px solid var(--rule);object-fit:contain;max-width:100%;max-height:70vh;display:block}.preview-inline-attachment{margin:18px 0 6px}.preview-inline-attachment[hidden]{display:none}.preview-attachment figcaption{color:var(--muted);font-family:var(--mono);margin-top:6px;font-size:.76rem}.preview-image-note{color:var(--muted);font-family:var(--mono);font-size:.76rem;display:inline-block}.unit{cursor:pointer;transition:background .12s linear,color .12s linear}.doc:not(.is-reading) .unit:hover,.doc:not(.is-reading) .unit:focus-visible,.doc:not(.is-reading) .unit.is-hover-group{text-underline-offset:5px;outline:none;text-decoration:underline;text-decoration-thickness:1px}.doc.is-reading .unit{cursor:text}.unit.is-pinned{background:var(--ink);color:var(--paper);padding:0 1px;text-decoration:none}.unit.is-pinned :is(code,a,strong,em){color:inherit}.pin{font-family:var(--mono);vertical-align:super;margin-left:3px;font-size:.62rem;font-weight:400}.pin-scope{gap:8px;margin:0 0 12px;display:flex}.pin-scope .chip:disabled{opacity:.4;cursor:default}.pin-term{box-sizing:border-box;border:1px solid var(--rule);width:100%;min-height:44px;font-family:var(--mono);margin:0 0 12px;padding:10px 12px;font-size:.92rem}.pin-siblings{border-top:1px solid var(--rule);flex-wrap:wrap;gap:8px;margin:12px 0 0;padding-top:12px;display:flex}.pin-sibling{border:1px solid var(--rule);min-height:44px;font-family:var(--mono);color:var(--muted);cursor:pointer;background:0 0;padding:0 12px;font-size:.78rem}.pin-siblings[hidden]{display:none}.source-panel{max-width:var(--column);border:1px solid var(--rule);margin:24px auto 0}.source-panel>summary{box-sizing:border-box;min-height:44px;font-family:var(--mono);color:var(--muted);cursor:pointer;align-items:center;padding:12px 14px;font-size:.78rem;display:flex}.source-text{border-top:1px solid var(--rule);font-family:var(--serif);color:var(--muted);white-space:pre-wrap;word-break:keep-all;overflow-wrap:anywhere;padding:14px;font-size:.98rem;line-height:1.85}.review-bar{z-index:20;padding:12px max(16px, calc((100vw - var(--column)) / 2));padding-bottom:calc(12px + var(--safe-bottom));background:var(--ink);color:var(--paper);letter-spacing:.08em;align-items:center;gap:16px;font-size:.72rem;display:flex;position:fixed;bottom:0;left:0;right:0}.bar-mode{text-transform:uppercase}.bar-count{color:#b3b3b3;margin-right:auto}.bar-add,.bar-send,.bar-finish,.bar-copy{cursor:pointer;letter-spacing:.08em;white-space:nowrap;min-height:30px;padding:0 14px;font-size:.72rem}.bar-add,.bar-finish,.bar-copy{color:var(--paper);background:0 0;border:1px solid #6b6b6b}.bar-send{border:1px solid var(--paper);background:var(--paper);color:var(--ink)}.bar-add:disabled,.bar-send:disabled,.bar-finish:disabled,.bar-copy:disabled{color:#6b6b6b;cursor:default;background:0 0;border-color:#4d4d4d}.review-bar .status{color:#b3b3b3;margin:0}.pin-card{left:0;right:0;bottom:var(--sheet-bottom);z-index:19;max-height:var(--sheet-max-height);padding:22px max(16px, calc((100vw - var(--column)) / 2)) 26px;background:var(--ink);color:var(--paper);border-top:1px solid #4d4d4d;transition:bottom .16s ease-out,max-height .16s ease-out;animation:.16s ease-out sheet-up;position:fixed;overflow-y:auto}@keyframes sheet-up{0%{opacity:0;transform:translateY(12px)}}.pin-card-head{letter-spacing:.16em;text-transform:uppercase;justify-content:space-between;align-items:baseline;margin-bottom:12px;font-size:.68rem;display:flex}.pin-card-close{min-width:30px;min-height:30px;color:var(--paper);cursor:pointer;background:0 0;border:0;font-size:1rem}.pin-quote{border-left:2px solid var(--paper);font-family:var(--serif);max-height:7.2em;margin:0 0 20px;padding-left:12px;font-size:1rem;line-height:1.8;overflow-y:auto}.label{color:#b3b3b3;letter-spacing:.18em;text-transform:uppercase;margin:0 0 8px;font-size:.62rem}.chips{flex-wrap:wrap;gap:6px;margin-bottom:20px;display:flex}.chip{min-height:30px;color:var(--paper);cursor:pointer;background:0 0;border:1px solid #6b6b6b;padding:0 11px;font-size:.72rem}.chip.is-on{border-color:var(--paper);background:var(--paper);color:var(--ink)}.pin-correction{width:100%;min-height:76px;color:var(--paper);font-family:var(--serif);resize:vertical;background:0 0;border:1px solid #6b6b6b;border-radius:0;padding:10px 12px;font-size:.98rem}.pin-correction::placeholder{color:#767676}.pin-remember{align-items:center;gap:9px;min-height:42px;font-size:.72rem;display:flex}.pin-remember[hidden]{display:none}.pin-remember input{width:14px;height:14px;accent-color:var(--paper)}.pin-actions{align-items:center;gap:10px;margin:2px 0 18px;display:flex}.pin-actions[hidden]{display:none}.pin-remove{color:#b3b3b3;cursor:pointer;text-underline-offset:3px;background:0 0;border:0;min-height:34px;font-size:.72rem;text-decoration:underline}.pin-card .status{color:#b3b3b3;margin:10px 0 0;font-size:.72rem}.pin-actions .status{margin:0}.terminal-state{max-width:var(--column);margin:22vh auto 0}.terminal-state h1{letter-spacing:.2em;text-transform:uppercase;margin:0 0 10px;font-size:.78rem;font-weight:400}.terminal-state p,.status{color:var(--muted);margin:0;font-size:.78rem}@media (width<=720px){.review-bar{padding-top:6px;padding-left:12px;padding-right:12px;padding-bottom:calc(6px + var(--safe-bottom));gap:6px}.bar-add,.bar-send,.bar-finish,.bar-copy{padding-left:10px;padding-right:10px}.doc{font-size:1.02rem;line-height:1.95}.pin-card{padding-bottom:12px;padding-left:16px;padding-right:16px}.pin-actions{margin-bottom:0}}@media (pointer:coarse){.chip,.pin-card-close,.pin-remove,.bar-add,.bar-send,.bar-finish,.bar-copy,.mode-toggle{min-height:44px}.chip{padding:0 14px}.pin-card-close{min-width:44px}}@media (prefers-reduced-motion:reduce){*,:before,:after{scroll-behavior:auto!important;transition-duration:.001ms!important;animation-duration:.001ms!important}}
|