vccs
Vue 3용 차트 컴포넌트 모음, Recharts에서 포팅
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Local Vite + React app that reliably extracts structured logic models from PDF/DOCX/PPTX via Gemini for downstream processing, supports human editing, and exports CSV / branded PDFs.
1. npm install
2. Set GEMINI_API_KEY in .env.local
3. npm run dev — Express API (:3011) + Vite (:3000)
Builds the client, then serves API + dist from Express on :3011 (override with PORT).README 전문 보기 ↗Local Vite + React app that reliably extracts structured logic models from PDF/DOCX/PPTX via Gemini for downstream processing, supports human editing, and exports CSV / branded PDFs.
npm installGEMINI_API_KEY in .env.localnpm run dev — Express API (:3011) + Vite (:3000)npm start
Builds the client, then serves API + dist from Express on :3011 (override with PORT).
npm run dev — API + Vite clientnpm run build — client onlynpm run typecheck — tsc --noEmitnpm run replay — replay post-processing over saved model answers, offline and free (-- --score to also score it)npm run benchmark:accuracy — extract the synthetic benchmark documents and score them (-- --capture first time)npm run split:survey — every blessed snapshot cell that the run-on splitter would divide, and into whatnpm start — build + production serverBoth Gemini calls read their model from server/geminiModelConfig.ts. The default is the rolling
alias gemini-flash-latest, because a dated snapshot gets sunset for new API keys —
gemini-2.5-flash already returns 404 "no longer available to new users" on a freshly created key.
The price of a rolling alias is that Google can change what answers with no change here, and on
2026-10-07 it did: the alias moved to gemini-3.8-flash and extraction accuracy moved with it. So
two things are true of this codebase and should stay true.
The model is configuration, not code. Override either call without touching a file:
LM_EXTRACT_MODEL=gemini-3.7-flash npm run benchmark:accuracy # score a candidate
LM_EXTRACT_MODEL=gemini-3.7-flash npm run regression:check # the real set, on his machine
LM_DETECT_MODEL=gemini-3.8-flash npm run dev # page-group detection only
LM_EXTRACT_THINKING_LEVEL is there for the same reason, as an experiment knob with no shipped
effect: unset (and =default) sends no thinkingConfig at all, which is the measured default —
thinkingLevel: low scored the invented benchmark at 100% and cost two real documents accuracy
(PR #36, reverted by #37). An unrecognised value refuses to start rather than quietly running the
default and filing its numbers under the candidate's name.
A blank value means unset. CANDIDATE_MODEL_IDS in that module is the short list worth scoring
when the served model changes — a list, deliberately, because picking one model and tuning to it is
the same bet in different clothes, due again at the next rotation. Membership means "worth
measuring", never "known good".
Every result records which model produced it. served_model_id in the extraction-log CSV, the
servedModelId field on regression snapshots and in benchmark run records, and the LM_DUMP_RAW
dumps all carry response.modelVersion — what answered, as distinct from the alias we asked for.
Absent means unknown and never "the same as the alias". npm run regression:check says out loud
when a baseline was captured on a different served model than the one answering, because a diff
spanning a model change attributes nothing.
What none of this does is make extraction model-independent. The prompt is tuned against observed
model behaviour and extraction quality is a property of the model, so a model change always needs
measuring on fixtures/regression-set/ as well as the benchmark. What it removes is the cost of
measuring it.
npm run benchmark:accuracy -- --input=text-only # send no page images, text track only
npm run regression:check -- --input=text-only # same, on the real set (never blesses)
text-only asks what the page rasters buy; see scripts/inputArm.ts for what it cannot see. The
prompt variant on every row reads text-only, so its results cannot pool with the default.
Post-processing never moves an item between domains. It may fix shape (colons, inline labels,
run-on cells, exact restated duplicates) and fill an empty field from labelled source text, and it
reports anything it would once have moved as a review blocker. See normalizeExtractedLogicModel.
The same API is exposed two ways so one codebase serves both targets:
server.ts) on :3011api/ (/api/health, /api/gemini/extract,
/api/gemini/detect-logic-models, /api/convert/pptx-to-pdf)The Gemini extract and detect routes delegate to shared handlers in server/apiCore.ts, so their
request/response behavior stays identical between the two paths. /api/health and the PPTX convert
route diverge: /api/health shares only getApiKey() — server.ts's handler additionally reports
libreOfficeWasm and a 'production'|'development' mode, while api/health.ts omits
libreOfficeWasm entirely and reports mode from VERCEL_ENV || NODE_ENV || 'unknown'. The PPTX
convert route shares server/pptxConvertApi.ts's handlePptxToPdfRequest, but the two entry points
parse the request body differently (Express uses express.raw; the Vercel function hands the
handler whatever Vercel itself already parsed) — found via codebase audit
(docs/specs/codebase-audit-2026-09-19.md #18).
Setup:
GEMINI_API_KEY (Production + Preview).https<!-- -->://<your-app>/api/health returns {"ok":true,"configured":true,...}.PowerPoint needs two things on a hosted deployment, and both are easy to lose:
getLibreOfficeWasmPath() builds the path at runtime — so a bundler that ships a
function by tracing its imports leaves them out. vercel.json names them with includeFiles,
and gives that one function 2048MB: LibreOffice peaks at ~1.07GB converting a one-line document,
which is already over the 1024MB the route used to run with. 2048MB is the ceiling on a Hobby
account — a deployment that asks for more is rejected outright at build time — so a deck heavy
enough to need more than that has no headroom left to take.application/octet-stream, not the
OOXML presentation type: a serverless host parses the body by content type and hands the
function undefined for anything outside its short list. Sent honestly, the deck's bytes never
arrived at all and the route answered 400 before LibreOffice was reached. The function also
reads the request stream itself as a backstop.GET /api/convert/pptx-to-pdf answers whether that deployment can convert PowerPoint:
{"ok":true,"libreOfficeWasm":true}. It lives on the convert route rather than in /api/health
because each function is bundled separately, so only that function can answer for its own files.
Hosted limits to be aware of:
/api/gemini/extract).config/brand.tsdocs/specs/.cursor/rules/ — see AGENTS.md