Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
130 commits
Select commit Hold shift + click to select a range
a237963
Record which rule produced each braille cell
Sep 21, 2026
fca9f05
Wrap the trace summary the way the linter wants
Sep 21, 2026
d36a727
Cover the trace registry and the node trace binding
Sep 21, 2026
78e7fb7
Reach the last traced lines, and delete the two nothing reaches
Sep 21, 2026
ca8e554
Prove the ampersand needs a Roman word on both sides
Sep 21, 2026
27fa913
Let a label answered in Roman or figures take its blank
Sep 21, 2026
94d6403
Read past the Korean naming the figures a middle dot joins
Sep 21, 2026
495a7b1
Name the article behind every Korean and token rule
Sep 21, 2026
56243ae
Name the rule behind UEB capital and grade-1 indicators
Sep 21, 2026
37a26a7
Name the article behind every math symbol branch
Sep 21, 2026
39a84b3
Find five symbols' articles by matching cells, not characters
Sep 21, 2026
f7d8240
Claim the inline math that English prose carries
Sep 21, 2026
ea202e9
Accept three symbols the exam papers write and we refused
Sep 21, 2026
bc19e31
Read the ohm sign as the omega it is
Sep 21, 2026
ad81860
Read the half-width corner bracket as the bracket it is
Sep 21, 2026
1341f4e
Make the suite refuse a rule that never named its article
Sep 21, 2026
7ed62e6
Move three geometry marks to the article that defines them
Sep 21, 2026
77d050f
Test the summation blank and the multiplying middle dot
Sep 21, 2026
ec192f9
Write circled capitals the way 국립국어원 settled them
Sep 22, 2026
27ad897
Stop transcribing the n-ary product
Sep 22, 2026
a107694
Tell an arrow over two points from an arrow between them
Sep 22, 2026
de65efd
Record that the product sign no longer transcribes
Sep 22, 2026
14f7268
Name the brace that LaTeX writes with a one-sided delimiter
Sep 22, 2026
44aba6d
Send the ellipsis inside a formula to the math article
Sep 22, 2026
8817a73
Read the middle dot in a formula as multiplication
Sep 22, 2026
4e20d3e
Say which standard an article belongs to, not which engine ran
Sep 22, 2026
405d86c
Stop reading a Hanja as evidence of a historical text
Sep 22, 2026
0a165d8
Let a cell name every article that decided it
Sep 22, 2026
b371b94
Require a math rule to name its article
Sep 22, 2026
1c88d43
Collapse three expressions the coverage recorder cannot follow
Sep 22, 2026
852a7ef
Cover the attribution lines the rule trace added
Sep 22, 2026
6f3348d
Give the proportion sign its article and drop the sequence-brace stub
Sep 22, 2026
1f0b5f2
Satisfy the lint gate the last two commits tripped
Sep 22, 2026
543329d
Remove the raw-token fallback that nothing can reach
Sep 22, 2026
31fe770
Hand the trace across the wasm boundary as JSON
Sep 22, 2026
c02ab74
Credit the Nemeth switch indicators to the article that defines them
Sep 22, 2026
4fecdac
Let a record say where it wrote, and record the §8.8.2 capitals
Sep 22, 2026
aa49146
Rebase a word's records when its buffer joins the output
Sep 22, 2026
83c84af
Carry the rebase through the prose chunks of a nemeth line
Sep 22, 2026
08df1c0
Rebase the tail chunk of a nemeth line too
Sep 22, 2026
d913a2c
Let a word attempt say where it wrote, too
Sep 22, 2026
22261dc
Add the science fixtures for chemical formulas and reaction equations
Sep 22, 2026
c222a6d
Add the rest of the science fixtures and map what already works
Sep 22, 2026
e65011d
Correct the science fixture inputs the PDF shows as subscripts
Sep 22, 2026
733df6c
Add the sex signs of article 22 and drop the fixtures I invented
Sep 22, 2026
9e779ae
Spell a formula's element symbols letter by letter
Sep 22, 2026
1e295bf
Put the roman indicator back on a subscripted formula
Sep 22, 2026
6c09b0b
Write the element before its atomic number and mass number
Sep 22, 2026
98c04bb
Write a chain of elements as a structural formula
Sep 22, 2026
f6d6b28
Route a structural formula away from English, but not a spelled word
Sep 23, 2026
a8f3368
Write a galvanic cell the way science article 21 does
Sep 23, 2026
2a23a74
Hold the science fixtures to the same bar as Korean and maths
Sep 23, 2026
bedf977
Cover the chemistry branches the gate found unexercised
Sep 23, 2026
9da4068
Name every cell the English engine writes around lone capitals
Sep 23, 2026
af0ff83
Keep a function and its single argument as one fraction term
Sep 23, 2026
1cb8b6f
Write a slash-joined unit such as kgf/m2 as one Roman span
Sep 23, 2026
dc0a92d
Write chemical formulas and reactions the way science articles 1-8 do
Sep 23, 2026
1ce2316
Share one element table and move only isotope numbers behind the element
Sep 23, 2026
3a4f254
Add the science fixtures exactly as the PDF prints them
Sep 23, 2026
e3f443a
Let the conversion test fail and read multi-line braille
Sep 23, 2026
7eb3030
Add a science context for what only the science articles can read
Sep 23, 2026
bcf11a2
Keep consecutive capital genes out of the capital phrase in the scien…
Sep 23, 2026
66d95af
Write a dental formula the way science article 26 does
Sep 23, 2026
9c3e555
Decide four science fixtures by the science context and hold every sc…
Sep 23, 2026
240db29
Hand the chemical formula rule's word to its helpers instead of fetch…
Sep 23, 2026
c4dbb8a
Cover the arms chemistry routing stopped reaching
Sep 23, 2026
6be4b50
Drop the capital phrase in the science context only for runs of genes
Sep 23, 2026
f6fdc56
State exactly why the gravitational constant's four lines stay a limi…
Sep 23, 2026
d4c4017
Accept a reading context by name in the public API
Sep 23, 2026
78d57cc
Let the JS functions take an optional context and check context fixtu…
Sep 23, 2026
c484b0c
Give the Python functions an optional context keyword
Sep 23, 2026
f37d666
Add the in_context functions to the Ruby binding
Sep 23, 2026
1f761b0
Add the in_context functions to the C binding
Sep 23, 2026
57b9bf7
Add context overloads to the .NET binding
Sep 23, 2026
666f335
Add context overloads to the JVM binding
Sep 23, 2026
1f96ac0
Add the InContext functions to the Go binding
Sep 23, 2026
47d1aa7
Write two-dimensional structural formulas, electron dots and pedigrees
Sep 23, 2026
740dd1d
Add the PDF's two-dimensional science examples as multi-line fixtures
Sep 23, 2026
9737e5a
Read a single capital before a known molecule as a quantity symbol
Sep 23, 2026
27500c5
Drop the two fixture groups that hold no example
Sep 24, 2026
f05bdb7
Refresh the changepack note and status line for the rule trace and sc…
Sep 24, 2026
b027efc
Tell a tone mark from a middle dot, and drop the variation selector p…
Sep 24, 2026
d93a8da
Space a colon that runs into Korean content, whatever writes the label
Sep 24, 2026
af0bc0c
Write a run of superscripts after one superscript sign
Sep 24, 2026
9869278
Say which unit marks of the gravitational constant the rules explain
Sep 24, 2026
44f6c18
Refresh the changepack note and status line for this round
Sep 24, 2026
1fcd00b
Write nothing for a null right delimiter unless it closes the equatio…
Sep 24, 2026
a36ff84
Record that a pair of null delimiters writes only the operand
Sep 24, 2026
c8ae032
Add the null right delimiter to the changepack note
Sep 24, 2026
ecee110
Space a colon after a bracketed label, but not between mirrored items
Sep 24, 2026
83e7b58
Write a filled triangle that closes a word as the same list marker
Sep 24, 2026
32b61ba
Write structural formulas and electron dots in the spatial form when …
Sep 24, 2026
47a0157
Add the PDF's spatial-form diagrams as fixtures
Sep 24, 2026
acd3410
Write the units that follow a number inside a formula
Sep 24, 2026
b8f77a3
Accept the gravitational constant on one line and drop the last limit…
Sep 24, 2026
1f3fbd9
Refresh the changepack note and status line for the spatial form and …
Sep 24, 2026
08969eb
Drop the plus sign unit exponents never carry and fold the overlap gu…
Sep 24, 2026
648c610
Space a title colon in running text and keep a ratio's bracketed lege…
Sep 24, 2026
1f9e638
Fold the grave accent, dot above and heavy vertical line into the sym…
Sep 24, 2026
8e715e9
Write ring compounds and fused rings in the symbol form of 과학 제12·14항
Sep 24, 2026
28e2a4e
Refresh the changepack note and status line for the rings and the colon
Sep 24, 2026
5576d0d
Write the square and triangle rings and the square ring's spatial form
Sep 24, 2026
3c892b4
Read a plus after a number as a superscript only before 등급
Sep 24, 2026
2f1552e
Keep a Korean word's figure annotation with an arrow or middle dot in…
Sep 24, 2026
3c950c6
Keep bracketed figures and a slash after a Korean word out of the mat…
Sep 24, 2026
b2a19bf
Let a figure annotation carry a unit and a sign
Sep 24, 2026
c90623e
Split a colon or semicolon before the later phases see the word
Sep 24, 2026
4dce872
Close a bracket's print gap before the later phases see the words
Sep 24, 2026
0986cfc
Join words around a middle dot before the later phases see them
Sep 24, 2026
ae08d7c
Write a reference mark as an asterisk and space a note's heading aste…
Sep 24, 2026
6a0aa17
Revert "Join words around a middle dot before the later phases see them"
Sep 24, 2026
f98f1c1
Refresh the changepack note and status line for this round
Sep 24, 2026
f9b12f5
Count characters that carry no print as silent in the encode property…
Sep 24, 2026
1a40cc3
Remove the token rules the engine never ran
Sep 24, 2026
f15751f
Hand a word a rule leaves alone to the next rule of its phase
Sep 24, 2026
0a4b4e6
Keep Roman exclamations, acronym lists, unit ranges and figure annota…
Sep 24, 2026
15b3b38
Keep an open Roman gloss after a Korean word out of the math route
Sep 24, 2026
9faf0e9
Match the open-gloss test to the comma cell the encoder writes
Sep 24, 2026
b1921ed
Refresh the corpus figure in the changepack note and status line
Sep 24, 2026
f45cfac
Follow the punctuation spacing of the 한글 맞춤법 appendix and drop the do…
Sep 25, 2026
0eb0793
Abbreviate 제18항 words after punctuation, keep bracketed numbers in pr…
Sep 25, 2026
a60fc01
Join a slash or a digit hyphen spaced on one side only
Sep 25, 2026
47d2c80
Omit the terminator before punctuation that follows a digital address
Sep 25, 2026
3cac8d7
Stop a Korean bracket and a unit letter from reopening a closed Roman…
Sep 25, 2026
028bb7f
Keep a separated unit inside the Roman section its number continues
Sep 25, 2026
e214468
Write a Korean comma before a Korean number and a grade-1 sign before…
Sep 25, 2026
bcaf72d
Read a colon before a Korean number as Korean and keep a grade minus …
Sep 25, 2026
e0ca52a
Read a whole ratio or time group before choosing its colon
Sep 25, 2026
2801e46
Refresh the corpus figure in the changepack note and status line
Sep 25, 2026
427d016
Keep the grade-1 sign only on the unit path that reaches it
Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .changepacks/changepack_log_Hn4kRtBqWm2ZsVxLdOeYc.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"changes":{"libs/braillify/Cargo.toml":"Patch","packages/c/Cargo.toml":"Patch","packages/dotnet/Braillify/Braillify.csproj":"Patch","packages/dotnet/BraillifyNet/BraillifyNet.csproj":"Patch","packages/go/Cargo.toml":"Patch","packages/node/package.json":"Patch","packages/python/pyproject.toml":"Patch","packages/ruby/Cargo.toml":"Patch"},"note":"Apply the National Institute of Korean Language rulings on the grade-1 symbol and the ever contraction, read a whole-run groupsign as letters where the rules require it, separate a dash that opens the text, and join a spaced hyphen that is not a subtraction sign, and close the editorial gap inside a bracket; corpus accuracy 455,975 of 467,121 sentences.","date":"2026-09-21T04:00:00.0000000Z"}
{"changes":{"libs/braillify/Cargo.toml":"Minor","packages/c/Cargo.toml":"Minor","packages/dotnet/Braillify/Braillify.csproj":"Minor","packages/dotnet/BraillifyNet/BraillifyNet.csproj":"Minor","packages/go/Cargo.toml":"Minor","packages/jvm/build.gradle.kts":"Minor","packages/node/package.json":"Minor","packages/python/pyproject.toml":"Minor","packages/ruby/Cargo.toml":"Minor"},"note":"Apply the National Institute of Korean Language rulings on the grade-1 symbol, the ever contraction, circled capitals and the n-ary product, read a whole-run groupsign as letters where the rules require it, separate a dash that opens the text, join a spaced hyphen that is not a subtraction sign, and close the editorial gap inside a bracket; report which rule wrote every braille cell; write chemical formulas, reaction equations, structural formulas, electron dot formulas, ring compounds of every shape, fused rings and pedigrees by the science articles; write them in the spatial form in the science_spatial context, and write the units that follow a number inside a formula; let every binding take a reading context such as science; space a colon that runs into Korean content or closes a title in running text, fold the grave accent, dot above and heavy vertical line into the symbols they stand for, keep a Korean word's bracketed figures and a slash after a Korean word out of the math route, split a colon and close a bracket gap before the later rules see the words, write a lone reference mark as an asterisk, read a plus after a number as a superscript only in a grade, tell a tone mark from a middle dot, write a run of superscripts after one sign, write nothing for a LaTeX null right delimiter, keep Roman exclamations, acronym lists, unit ranges and open Roman glosses out of the math route, follow the punctuation spacing of the Korean orthography appendix, abbreviate the Article 18 words after punctuation, and keep units, grades and Korean-number punctuation in the right Roman section; corpus accuracy 456,626 of 467,121 sentences.","date":"2026-09-21T04:00:00.0000000Z"}
9 changes: 6 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

316 changes: 316 additions & 0 deletions apps/landing/src/app/RuleTrace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
'use client'

import { Box, Flex, Text, VStack } from '@devup-ui/react'

/** 한 번에 그리는 규칙 행의 최대 개수. 키 입력마다 다시 그리므로 상한을 둔다. */
const MAX_VISIBLE_RULES = 120

/** `kind` 원문 → 화면 표기. 모르는 값이면 원문을 그대로 보여준다. */
const KIND_LABEL: Record<string, string> = {
korean: '한글',
jamo: '자모',
token: '기호',
math: '수학',
'english-ueb': '영어',
emitter: '구조',
}

/**
* 규칙이 하나도 잡히지 않는 경로별 설명. 점역 자체는 정상이므로
* "적용된 규칙이 없다"고 읽히면 안 된다.
*/
const NO_RULE_NOTICE: Record<string, string> = {
'english-ueb':
'이 낱말은 축약 규칙 탐색이 아니라 낱말 기호표로 점역되어, 규칙 단위로 나눌 수 없습니다.',
}

/** 출력 점자의 일부를 만들어 낸 규칙 하나. WASM 객체를 평범한 값으로 옮긴 것. */
export interface TraceRule {
section: string
standardRef: string
name: string
description: string
kind: string
start: number
end: number
braille: string
}

/**
* 한 번의 점역 결과 스냅샷.
*
* - `idle` — 입력이 없거나 WASM이 아직 로드되지 않음. 아무것도 그리지 않는다.
* - `failed` — 점역이 실패함. 출력 상자가 이미 사유를 보여주므로 목록은 숨긴다.
* - `ok` — 점역 성공. 목록을 그린다.
*/
export interface TraceSnapshot {
status: 'idle' | 'ok' | 'failed'
braille: string
rules: TraceRule[]
/** 규칙이 설명하는 출력 칸 수. */
attributed: number
/** 전체 출력 칸 수. `attributed`보다 크면 추적되지 않은 칸이 있다는 뜻이다. */
total: number
/** 입력을 처리한 엔진: `korean` | `english-ueb` | `math`. */
path: string
}

export const IDLE_TRACE: TraceSnapshot = {
status: 'idle',
braille: '',
rules: [],
attributed: 0,
total: 0,
path: '',
}

export const FAILED_TRACE: TraceSnapshot = {
status: 'failed',
braille: '점역할 수 없는 문자가 있습니다.',
rules: [],
attributed: 0,
total: 0,
path: '',
}

interface RuleSpanJson {
section: string
subsection: string
standard_ref: string
name: string
description: string
kind: string
start: number
end: number
braille: string
}

interface TraceResultJson {
braille: string
rules: RuleSpanJson[]
attributed: number
total: number
path: string
}

/**
* 점역 결과를 JSON으로 받는다. WASM 쪽은 문자열 하나만 넘기므로 해제할 핸들이
* 없고, 렌더 중에 WASM 메모리를 다시 읽는 일도 없다.
*/
export function readTrace(json: string): TraceSnapshot {
const result = JSON.parse(json) as TraceResultJson
return {
status: 'ok',
braille: result.braille,
attributed: result.attributed,
total: result.total,
path: result.path,
rules: result.rules.map((span) => ({
section: span.section,
standardRef: span.standard_ref,
name: span.name,
description: span.description,
kind: span.kind,
start: span.start,
end: span.end,
braille: span.braille,
})),
}
}

/**
* 항 번호 표기. `-`는 규정 항이 없는 구조 출력이라 `제N항`으로 꾸며내지 않는다.
*
* 영어는 한국 점자 규정이 아니라 UEB 규정을 따르므로 `제N항`이 아닌 `§N` 표기를
* 쓴다. 수학은 같은 규정 안의 별도 장이라 한글 제N항과 번호가 겹치므로 `수학`을
* 붙여 구분한다. 번호 체계가 다른 규정을 같은 꼴로 적으면 출처를 잘못 읽게 된다.
*
* 규칙이 도는 엔진과 규칙이 구현하는 규정의 계열은 서로 다를 수 있다. 동그라미
* 숫자는 수식 안에서 만나도 한글 제64항이고, 수학 제64항은 햇(단위 벡터)이다.
* 그래서 계열은 엔진(`kind`)이 아니라 규칙이 밝힌 출처(`standardRef`)에서 읽는다.
*
* 한 판단이 여러 항에 걸칠 때는 `, `로 이어 적는다. 국립국어원 회신(2026-09-21)이
* 그런 경우 항을 하나만 고르지 말고 "다 적으라"고 했다.
*/
function sectionLabel(
section: string,
kind: string,
standardRef: string,
): string | null {
if (section === '-') return null

const series = standardRef.includes('한글 제')
? '한글 '
: standardRef.includes('수학 제')
? '수학 '
: kind === 'math'
? '수학 '
: ''

if (kind === 'english-ueb' && series === '') {
return section
.split(', ')
.map((one) => `§${one}`)
.join('·')
}
return section
.split(', ')
.map((one) => `${series}제${one}항`)
.join('·')
}

/** 반열린 구간 `[start, end)`를 1부터 세는 사람 기준 표기로 옮긴다. */
function rangeLabel(start: number, end: number): string {
if (end - start <= 1) return `${start + 1}번째 칸`
return `${start + 1}–${end}번째 칸`
}

/** 빈 칸(U+2800)도 눈에 보이도록 칸 하나를 칩으로 그린다. */
function BrailleCells({ braille }: { braille: string }) {
return (
<Flex flexWrap="wrap" gap="3px">
{Array.from(braille).map((cell, index) => (
<Text
key={`${cell}-${index}`}
bg="$background"
borderRadius="6px"
color="$text"
minW={['20px', null, null, '26px']}
px="4px"
textAlign="center"
typography="braille"
>
{cell}
</Text>
))}
</Flex>
)
}

function RuleRow({ rule }: { rule: TraceRule }) {
const section = sectionLabel(rule.section, rule.kind, rule.standardRef)

return (
<Flex
alignItems={[null, null, null, 'center']}
as="li"
borderTop="1px solid $border"
flexDirection={['column', null, null, 'row']}
gap={['8px', null, null, '16px']}
py={['12px', null, null, '14px']}
>
<Box flex="none" w={[null, null, null, '104px']}>
{section ? (
<Text
border="1px solid $border"
borderRadius="1000px"
color="$caption"
display="inline-block"
px="10px"
py="4px"
typography="tinyBtn"
whiteSpace="nowrap"
>
{section}
</Text>
) : null}
</Box>
<VStack flex="1" gap="2px" minW="0">
<Text color="$text" typography="bodyBold" wordBreak="break-all">
{rule.name}
</Text>
<Text color="$caption" typography="footer" wordBreak="break-word">
{rule.description} · {KIND_LABEL[rule.kind] ?? rule.kind}
</Text>
</VStack>
<Box flex="none">
<BrailleCells braille={rule.braille} />
</Box>
<Text
color="$caption"
flex="none"
textAlign={[null, null, null, 'right']}
typography="footer"
w={[null, null, null, '120px']}
whiteSpace="nowrap"
>
{rangeLabel(rule.start, rule.end)}
</Text>
</Flex>
)
}

/** 점역 결과를 만들어 낸 규칙 목록. 추적은 아직 부분적이라 덮인 범위를 함께 밝힌다. */
export function RuleTrace({ trace }: { trace: TraceSnapshot }) {
if (trace.status !== 'ok') return null

const isPartial = trace.attributed < trace.total
const visibleRules = trace.rules.slice(0, MAX_VISIBLE_RULES)
const hiddenCount = trace.rules.length - visibleRules.length
const emptyNotice =
trace.rules.length > 0
? null
: (NO_RULE_NOTICE[trace.path] ??
'이 입력에 대해 기록된 규칙이 아직 없습니다.')

return (
<VStack
bg="$containerBackground"
borderRadius={['16px', null, null, '30px']}
gap={['12px', null, null, '20px']}
p={['16px', null, null, '40px']}
w="100%"
>
<Flex
alignItems={[null, null, null, 'baseline']}
flexDirection={['column', null, null, 'row']}
gap={['4px', null, null, '12px']}
justifyContent="space-between"
>
<Text color="$text" typography="featureTitle">
적용 규칙
</Text>
{trace.total > 0 ? (
<Text color="$caption" typography="footer" whiteSpace="nowrap">
{trace.attributed}/{trace.total}칸 추적됨
</Text>
) : null}
</Flex>

{isPartial ? (
<Text color="$caption" typography="body" wordBreak="keep-all">
출력 {trace.total}칸 가운데 {trace.attributed}칸만 규칙으로
설명됩니다. 나머지 {trace.total - trace.attributed}칸은 아직 규칙
추적이 붙지 않은 부분입니다.
</Text>
) : null}
{emptyNotice ? (
<Text color="$caption" typography="body" wordBreak="keep-all">
{emptyNotice}
</Text>
) : (
<Flex
as="ul"
flexDirection="column"
listStyle="none"
m="0"
maxH={['320px', null, null, '420px']}
overflowY="auto"
p="0"
>
{visibleRules.map((rule, index) => (
<RuleRow
key={`${rule.name}-${rule.start}-${rule.end}-${index}`}
rule={rule}
/>
))}
</Flex>
)}
{hiddenCount > 0 ? (
<Text color="$caption" typography="footer" wordBreak="keep-all">
규칙 {hiddenCount}개는 목록에 표시하지 않았습니다.
</Text>
) : null}
</VStack>
)
}
Loading
Loading