Skip to content

Commit fd243f0

Browse files
authored
feat: rename BOXED_TEXT tool type to COMB_TEXT (#35)
## Background The editor renamed its `BOXED_TEXT` comb-field type to `COMB_TEXT` ("comb" is the Acrobat / PDF-spec term for box-per-character fields like IBAN, dates, CERFA). The embed artifacts still referenced the old name. The editor accepts both values at runtime, so this is a type/naming change, not a runtime break. ## Changes - Replace `BOXED_TEXT` with `COMB_TEXT` in the react `ToolType` (README + test updated) - Add a major changeset for `@simplepdf/react-embed-pdf` - Rename comb references across the copilot (code, schemas, types, tools, toolbar) and all 23 locale keys (`boxedText` -> `combText`, en label "Comb text") - Update `documentation/IFRAME.md` and the `with-custom-sidebar` example - Fix the pre-existing copilot `biome check` failures (formatting + `useOptionalChain`) and add two justified `biome-ignore`s so `check` is green ## Notes Runtime is backwards-compatible: the editor still accepts `BOXED_TEXT`, so already-deployed embeds keep working; only the TypeScript type changes.
1 parent f6042db commit fd243f0

39 files changed

Lines changed: 111 additions & 69 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"@simplepdf/react-embed-pdf": major
3+
---
4+
5+
Renames the `BOXED_TEXT` tool type to `COMB_TEXT`. "Comb" is the Acrobat / PDF-spec term for box-per-character fields (IBAN, dates, CERFA), so `actions.selectTool(...)` and the `SELECT_TOOL` iframe event now use `COMB_TEXT`, and the `ToolType` union exposes `COMB_TEXT` instead of `BOXED_TEXT`.
6+
7+
Already-deployed embeds keep working without a code change: the editor still accepts the legacy `BOXED_TEXT` value at runtime, so this only changes the TypeScript type. If you are not calling `actions.selectTool('BOXED_TEXT')` or `sendEvent("SELECT_TOOL", { tool: "BOXED_TEXT" })`, you can safely update to this new major version.
8+
9+
```ts
10+
// Before
11+
await actions.selectTool('BOXED_TEXT');
12+
13+
// After
14+
await actions.selectTool('COMB_TEXT');
15+
```

copilot/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ The chat sidebar advertises these tools to the model. Each runs inside the ifram
193193
| `detect_fields` | Auto-detect missing fields on scanned PDFs |
194194
| `focus_field` | Highlight + scroll to a field |
195195
| `set_field_value` | Write a value into a field |
196-
| `select_tool` | Switch the editor toolbar (`TEXT`, `BOXED_TEXT`, `CHECKBOX`, `SIGNATURE`, `PICTURE`) |
196+
| `select_tool` | Switch the editor toolbar (`TEXT`, `COMB_TEXT`, `CHECKBOX`, `SIGNATURE`, `PICTURE`) |
197197
| `go_to_page` | Navigate to a specific page (1-indexed) |
198198
| `move_page` | Reorder a visible page (`from_page``to_page`, both 1-indexed). Destructive — only fired on explicit user request |
199199
| `delete_page` | Remove a visible page and its fields (last remaining page can't be deleted). Destructive — only fired on explicit user request |

copilot/src/components/chat/chat_pane.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ type CompactedDocumentContent = { name: string | null; pages: DocumentContentPag
138138
const isToolbarTool = (value: unknown): value is ToolbarTool =>
139139
value === null ||
140140
value === 'TEXT' ||
141-
value === 'BOXED_TEXT' ||
141+
value === 'COMB_TEXT' ||
142142
value === 'CHECKBOX' ||
143143
value === 'SIGNATURE' ||
144144
value === 'PICTURE'
@@ -156,7 +156,7 @@ type PlacementTool = Exclude<ToolbarTool, null>
156156
// dedupes for icon rendering.
157157
type NewFieldHintMetadata = { kind: 'new_field_hint'; tools: PlacementTool[]; delta: number }
158158

159-
const PLACEMENT_TOOLS: readonly PlacementTool[] = ['TEXT', 'BOXED_TEXT', 'CHECKBOX', 'SIGNATURE', 'PICTURE']
159+
const PLACEMENT_TOOLS: readonly PlacementTool[] = ['TEXT', 'COMB_TEXT', 'CHECKBOX', 'SIGNATURE', 'PICTURE']
160160
const isPlacementTool = (value: unknown): value is PlacementTool =>
161161
typeof value === 'string' && PLACEMENT_TOOLS.some((candidate) => candidate === value)
162162

@@ -174,7 +174,9 @@ const buildNewFieldMessage = ({
174174
if (uniqueTools.length === 1) {
175175
return `${delta} new ${uniqueTools[0]} fields were just added to the document. Please continue helping me with them.`
176176
}
177-
const breakdown = uniqueTools.map((tool) => `${tools.filter((t) => t === tool).length} ${tool}`).join(', ')
177+
const breakdown = uniqueTools
178+
.map((tool) => `${tools.filter((t) => t === tool).length} ${tool}`)
179+
.join(', ')
178180
return `${delta} new fields were just added to the document (${breakdown}). Please continue helping me with them.`
179181
})()
180182
return { text, metadata: { kind: 'new_field_hint', tools, delta } }
@@ -859,6 +861,7 @@ export const ChatPane = ({
859861
// the visible width. Reset height to 'auto' first so scrollHeight reflects
860862
// the wrapped content, then clamp to MAX. The CSS transition on the element
861863
// animates the height change.
864+
// biome-ignore lint/correctness/useExhaustiveDependencies: `draft` is a deliberate re-run trigger (the body reads scrollHeight, not draft) so the textarea resizes on every keystroke; removing it freezes the height.
862865
useLayoutEffect(() => {
863866
const textarea = inputRef.current
864867
if (textarea === null) {

copilot/src/components/chat/toolbar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type ToolbarProps = {
1919
onFinalisation: () => void
2020
}
2121

22-
const BoxedTextIcon = ({ size = 14 }: { size?: number; strokeWidth?: number }) => (
22+
const CombTextIcon = ({ size = 14 }: { size?: number; strokeWidth?: number }) => (
2323
<svg
2424
viewBox="0 0 5515 4463"
2525
width={size}
@@ -48,7 +48,7 @@ export const TOOLBAR_OPTIONS: ToolOption[] = [
4848
{ value: 'CHECKBOX', labelKey: 'toolbar.checkbox', icon: Check },
4949
{ value: 'SIGNATURE', labelKey: 'toolbar.signature', icon: PenTool },
5050
{ value: 'PICTURE', labelKey: 'toolbar.picture', icon: ImageIcon },
51-
{ value: 'BOXED_TEXT', labelKey: 'toolbar.boxedText', icon: BoxedTextIcon },
51+
{ value: 'COMB_TEXT', labelKey: 'toolbar.combText', icon: CombTextIcon },
5252
]
5353

5454
export const Toolbar = ({

copilot/src/lib/embed-bridge-adapters/client-tools/tools.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ import {
2323
// in factory.ts. The switch is exhaustive over ClientToolName, so any
2424
// addition forces a matching arm at compile time.
2525

26-
const tool = <TSchema extends z.ZodType>(inputSchema: TSchema): { description: string; inputSchema: TSchema } => ({
26+
const tool = <TSchema extends z.ZodType>(
27+
inputSchema: TSchema,
28+
): { description: string; inputSchema: TSchema } => ({
2729
description: inputSchema.description ?? '',
2830
inputSchema,
2931
})

copilot/src/lib/embed-bridge/bridge.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,7 @@ export const createBridge = ({
122122
notify()
123123
}
124124

125-
const sendRequest = <TData>(
126-
type: BridgeRequestType,
127-
data: unknown,
128-
): Promise<BridgeResult<TData>> =>
125+
const sendRequest = <TData>(type: BridgeRequestType, data: unknown): Promise<BridgeResult<TData>> =>
129126
new Promise((resolve) => {
130127
const iframe = getIframe()
131128
if (iframe === null || iframe.contentWindow === null) {
@@ -407,20 +404,23 @@ export const createBridge = ({
407404
getState: () => state,
408405
loadDocument: (args) => parseAndSend(LoadDocumentInput, 'LOAD_DOCUMENT', args),
409406
getFields: () => sendRequest<{ fields: FieldRecord[] }>('GET_FIELDS', {}),
410-
getDocumentContent: (args) => parseAndSend<typeof GetDocumentContentInput, DocumentContentResult>(
411-
GetDocumentContentInput,
412-
'GET_DOCUMENT_CONTENT',
413-
args,
414-
),
407+
getDocumentContent: (args) =>
408+
parseAndSend<typeof GetDocumentContentInput, DocumentContentResult>(
409+
GetDocumentContentInput,
410+
'GET_DOCUMENT_CONTENT',
411+
args,
412+
),
415413
detectFields: () => sendRequest('DETECT_FIELDS', {}),
416-
deleteFields: (args) => parseAndSend<typeof DeleteFieldsInput, { deleted_count: number }>(
417-
DeleteFieldsInput,
418-
'DELETE_FIELDS',
419-
args,
420-
),
414+
deleteFields: (args) =>
415+
parseAndSend<typeof DeleteFieldsInput, { deleted_count: number }>(
416+
DeleteFieldsInput,
417+
'DELETE_FIELDS',
418+
args,
419+
),
421420
selectTool: (args) => parseAndSend(SelectToolInput, 'SELECT_TOOL', args),
422421
setFieldValue: (args) => parseAndSend(SetFieldValueInput, 'SET_FIELD_VALUE', args),
423-
focusField: (args) => parseAndSend<typeof FocusFieldInput, FocusFieldResult>(FocusFieldInput, 'FOCUS_FIELD', args),
422+
focusField: (args) =>
423+
parseAndSend<typeof FocusFieldInput, FocusFieldResult>(FocusFieldInput, 'FOCUS_FIELD', args),
424424
goTo: (args) => parseAndSend(GoToInput, 'GO_TO', args),
425425
movePage: (args) => parseAndSend(MovePageInput, 'MOVE_PAGE', args),
426426
deletePages: (args) => parseAndSend(DeletePagesInput, 'DELETE_PAGES', args),

copilot/src/lib/embed-bridge/schemas.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { z } from 'zod'
1010
// One file, one schema per operation. The shape is the snake_case payload
1111
// that travels over postMessage; nothing converts keys between layers.
1212

13-
export const SupportedFieldTypeSchema = z.enum(['TEXT', 'BOXED_TEXT', 'CHECKBOX', 'PICTURE', 'SIGNATURE'])
13+
export const SupportedFieldTypeSchema = z.enum(['TEXT', 'COMB_TEXT', 'CHECKBOX', 'PICTURE', 'SIGNATURE'])
1414

1515
export const NoInput = z.object({})
1616

@@ -28,19 +28,29 @@ export const DetectFieldsInput = NoInput.describe(
2828

2929
export const DeleteFieldsInput = z
3030
.object({
31-
field_ids: z.array(z.string()).optional().describe('Specific field identifiers to delete (omit to target by page or all)'),
32-
page: z.number().int().positive().optional().describe('1-indexed visible page to clear (omit to target specific ids or all)'),
31+
field_ids: z
32+
.array(z.string())
33+
.optional()
34+
.describe('Specific field identifiers to delete (omit to target by page or all)'),
35+
page: z
36+
.number()
37+
.int()
38+
.positive()
39+
.optional()
40+
.describe('1-indexed visible page to clear (omit to target specific ids or all)'),
3341
})
3442
.describe(
3543
'Deletes fields from the document. Pass field_ids to delete specific fields, page to clear a single page, or both omitted to delete every field. Destructive: only call when the user explicitly asks.',
3644
)
3745

3846
export const SelectToolInput = z
3947
.object({
40-
tool: SupportedFieldTypeSchema.nullable().describe('Editor tool to activate. Pass null to return to the cursor.'),
48+
tool: SupportedFieldTypeSchema.nullable().describe(
49+
'Editor tool to activate. Pass null to return to the cursor.',
50+
),
4151
})
4252
.describe(
43-
'Switches the active editor tool. Use tool="TEXT" for free-form text, "BOXED_TEXT" for box-per-character fields (e.g. IBAN), or any of the other field types to let the user drop fields on a document without native AcroFields.',
53+
'Switches the active editor tool. Use tool="TEXT" for free-form text, "COMB_TEXT" for box-per-character fields (e.g. IBAN), or any of the other field types to let the user drop fields on a document without native AcroFields.',
4454
)
4555

4656
export const SetFieldValueInput = z
@@ -50,7 +60,7 @@ export const SetFieldValueInput = z
5060
.string()
5161
.nullable()
5262
.describe(
53-
'Value to write. TEXT/BOXED_TEXT: any string. CHECKBOX: "checked" ticks, null un-ticks (never "true"/"false"). Do not use this tool for SIGNATURE or PICTURE fields.',
63+
'Value to write. TEXT/COMB_TEXT: any string. CHECKBOX: "checked" ticks, null un-ticks (never "true"/"false"). Do not use this tool for SIGNATURE or PICTURE fields.',
5464
),
5565
})
5666
.describe('Writes a value into a single field in the PDF')

copilot/src/lib/embed-bridge/types.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77
// in the union preserves IDE autocomplete for the bridge-owned literals
88
// while still accepting arbitrary forwarded codes — narrowing on a
99
// specific iframe code stays the consumer's responsibility.
10-
type BridgeOwnedErrorCode = 'bad_input' | 'bridge_disposed' | 'iframe_not_ready' | 'missing_result' | 'timeout'
10+
type BridgeOwnedErrorCode =
11+
| 'bad_input'
12+
| 'bridge_disposed'
13+
| 'iframe_not_ready'
14+
| 'missing_result'
15+
| 'timeout'
1116

1217
export type BridgeErrorCode = BridgeOwnedErrorCode | (string & {})
1318

@@ -52,7 +57,7 @@ export const isBridgeResultLike = (value: unknown): value is BridgeResult<unknow
5257
return false
5358
}
5459

55-
export type SupportedFieldType = 'TEXT' | 'BOXED_TEXT' | 'CHECKBOX' | 'PICTURE' | 'SIGNATURE'
60+
export type SupportedFieldType = 'TEXT' | 'COMB_TEXT' | 'CHECKBOX' | 'PICTURE' | 'SIGNATURE'
5661

5762
export type FieldRecord = {
5863
field_id: string

copilot/src/locales/ar.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163
"checkbox": "مربع اختيار",
164164
"signature": "توقيع",
165165
"picture": "صورة",
166-
"boxedText": "نص في خانات",
166+
"combText": "نص في خانات",
167167
"download": "تنزيل",
168168
"submit": "إرسال"
169169
},

copilot/src/locales/cs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
"checkbox": "Zaškrtávací políčko",
158158
"signature": "Podpis",
159159
"picture": "Obrázek",
160-
"boxedText": "Text v polích",
160+
"combText": "Text v polích",
161161
"download": "Stáhnout",
162162
"submit": "Odeslat"
163163
},

0 commit comments

Comments
 (0)