Skip to content

Commit dbace25

Browse files
asgeirfclaude
andauthored
fix(proto): compile the split neokapi proto pair (content.proto + bridge) (#17)
neokapi moved the content-model messages verbatim into core/proto/content/v1/content.proto (same field numbers, same java_package), which the bridge proto now imports. The proto sync/build now carries both files; generated Java class names are unchanged. Claude-Session: https://claude.ai/code/session_019EWn2MKChfryUs7LBQz9az Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 3b909c1 commit dbace25

2 files changed

Lines changed: 408 additions & 285 deletions

File tree

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
// content.proto — the canonical wire schema for the neokapi content model
2+
// (Part / Block / Run / Overlay / Target / Skeleton — see AD-002 and AD-034).
3+
//
4+
// These messages were promoted VERBATIM from
5+
// core/plugin/proto/v2/neokapi_bridge.proto — same message shapes, same field
6+
// names, same field numbers — so the wire encoding of every field is
7+
// unchanged. Only the proto package (and thus the fully-qualified type names,
8+
// which matter for google.protobuf.Any packing and descriptor reflection,
9+
// neither of which the repo uses on these messages) changed.
10+
//
11+
// Compatibility policy:
12+
// - Field numbers are FROZEN. Never renumber or repurpose a field.
13+
// - Never rename fields (protojson and cross-language peers depend on names).
14+
// - New fields append with fresh numbers; removed fields are reserved.
15+
// - The Java okapi-bridge compiles this file alongside the bridge service
16+
// proto; java_package stays "neokapi.bridge.proto" so the generated Java
17+
// class names (and thus the Java bridge sources) are unchanged.
18+
//
19+
// Every other serialization of Blocks/Runs in the repo is either an import of
20+
// this schema or an explicitly-labeled lossy projection (BlockIndex,
21+
// ContentTree, structrec.Record, the bowrain editor proto). A guard test
22+
// (core/proto/content/guard_test.go) rejects new Block/Run message
23+
// definitions outside this file.
24+
25+
syntax = "proto3";
26+
27+
package neokapi.content.v1;
28+
29+
option go_package = "github.com/neokapi/neokapi/core/proto/content/v1;contentv1";
30+
option java_package = "neokapi.bridge.proto";
31+
option java_multiple_files = true;
32+
33+
// ────────────────────────────────────────────────────────────────────────────
34+
// Annotations
35+
// ────────────────────────────────────────────────────────────────────────────
36+
37+
// AnnotationEntry is a typed annotation with a JSON-encoded payload.
38+
message AnnotationEntry {
39+
string type = 1; // "note", "alt-translation", "its-lqi", "generic", etc.
40+
bytes data = 2; // JSON-encoded type-specific payload
41+
}
42+
43+
// RunRangeMessage is a run-anchored byte/run span (the position of a
44+
// overlay span).
45+
message RunRangeMessage {
46+
int32 start_run = 1;
47+
int32 start_offset = 2;
48+
int32 end_run = 3;
49+
int32 end_offset = 4;
50+
}
51+
52+
// VariantMessage identifies a target variant (locale + optional tone/channel).
53+
// Its absence on an OverlayMessage means the overlay is source-side.
54+
message VariantMessage {
55+
string locale = 1;
56+
string tone = 2;
57+
string channel = 3;
58+
}
59+
60+
// SpanMessage is one occurrence within an overlay: a run-anchored
61+
// range, optional string props, and the typed payload (carried as an
62+
// AnnotationEntry so unknown payload types degrade to a generic map, exactly
63+
// like block-scoped annotations).
64+
message SpanMessage {
65+
string id = 1;
66+
RunRangeMessage range = 2;
67+
map<string, string> props = 3;
68+
AnnotationEntry value = 4;
69+
}
70+
71+
// OverlayMessage is one stand-off overlay on a block: a typed, optionally
72+
// variant-scoped, layered set of spans. This carries the full overlay vocabulary
73+
// across the wire (term, entity, qa, alignment, and any plugin-defined
74+
// type), so an overlay type a peer doesn't recognise round-trips by type name and
75+
// JSON rather than being dropped. Segmentation overlays are NOT carried here —
76+
// they are reconstructed from the source/target SegmentMessage boundaries.
77+
message OverlayMessage {
78+
string type = 1; // overlay type ("term", "entity", "qa", …)
79+
VariantMessage variant = 2; // absent = source side
80+
string layer = 3; // "" = primary
81+
repeated SpanMessage spans = 4;
82+
}
83+
84+
// ────────────────────────────────────────────────────────────────────────────
85+
// Inline Markup (RFC 0001 Run model)
86+
// ────────────────────────────────────────────────────────────────────────────
87+
88+
// RunConstraints control whether an inline-code run may be dropped,
89+
// duplicated, or reordered by a translation.
90+
message RunConstraints {
91+
bool deletable = 1;
92+
bool cloneable = 2;
93+
bool reorderable = 3;
94+
}
95+
96+
// TextRunMessage is a plain text chunk.
97+
message TextRunMessage {
98+
string text = 1;
99+
}
100+
101+
// PlaceholderRunMessage is a self-closing inline code (variable,
102+
// conditional JSX expression, line break, icon, etc.).
103+
message PlaceholderRunMessage {
104+
string id = 1;
105+
string type = 2; // Vocabulary key, e.g. "jsx:var", "jsx:node", "html:br".
106+
string sub_type = 3; // Fine-grained discriminator.
107+
string data = 4; // Original source slice, preserved verbatim.
108+
string equiv = 5; // Stable human-friendly identifier (variable name, tag name).
109+
string disp = 6; // Display label for chips.
110+
RunConstraints constraints = 7;
111+
}
112+
113+
// PcOpenRunMessage is the opening half of a paired inline code.
114+
message PcOpenRunMessage {
115+
string id = 1;
116+
string type = 2;
117+
string sub_type = 3;
118+
string data = 4; // Raw opening source ("<span class=\"muted\">").
119+
string equiv = 5;
120+
string disp = 6;
121+
RunConstraints constraints = 7;
122+
}
123+
124+
// PcCloseRunMessage is the closing half of a paired inline code.
125+
// Shares id with its PcOpen inside the same runs scope.
126+
message PcCloseRunMessage {
127+
string id = 1;
128+
string type = 2;
129+
string sub_type = 3;
130+
string data = 4; // Raw closing source ("</span>").
131+
string equiv = 5;
132+
}
133+
134+
// SubRunMessage is a reference to a subblock (sub-filter output).
135+
message SubRunMessage {
136+
string id = 1;
137+
string ref = 2;
138+
string equiv = 3;
139+
}
140+
141+
// PluralRunMessage is a structured plural construct. Keys of `forms`
142+
// are ICU plural forms: "zero", "one", "two", "few", "many", "other".
143+
message PluralRunMessage {
144+
string pivot = 1;
145+
map<string, RunList> forms = 2;
146+
}
147+
148+
// SelectRunMessage is a structured select construct, symmetric to
149+
// PluralRunMessage but keyed by arbitrary case values.
150+
message SelectRunMessage {
151+
string pivot = 1;
152+
map<string, RunList> cases = 2;
153+
}
154+
155+
// RunList is a repeated-field holder used by map values in
156+
// PluralRunMessage and SelectRunMessage (proto3 maps cannot have
157+
// repeated-message values directly).
158+
message RunList {
159+
repeated RunMessage runs = 1;
160+
}
161+
162+
// RunMessage is the discriminated-union inline-content primitive.
163+
// Exactly one of the oneof fields is present per Run.
164+
message RunMessage {
165+
oneof kind {
166+
TextRunMessage text = 1;
167+
PlaceholderRunMessage ph = 2;
168+
PcOpenRunMessage pc_open = 3;
169+
PcCloseRunMessage pc_close = 4;
170+
SubRunMessage sub = 5;
171+
PluralRunMessage plural = 6;
172+
SelectRunMessage select = 7;
173+
}
174+
}
175+
176+
// ────────────────────────────────────────────────────────────────────────────
177+
// Segments & Targets
178+
// ────────────────────────────────────────────────────────────────────────────
179+
180+
// SegmentMessage represents a single segment within a Block.
181+
message SegmentMessage {
182+
string id = 1;
183+
repeated RunMessage runs = 2;
184+
map<string, string> properties = 3;
185+
}
186+
187+
// TargetEntry maps a locale to its target segments.
188+
message TargetEntry {
189+
string locale = 1;
190+
repeated SegmentMessage segments = 2;
191+
}
192+
193+
// ────────────────────────────────────────────────────────────────────────────
194+
// Lightweight Block (for gRPC transfer — no skeleton, no display hints)
195+
// ────────────────────────────────────────────────────────────────────────────
196+
197+
// ContentBlock is a lightweight representation of a translatable block
198+
// designed for efficient gRPC transfer. It carries only the fields that
199+
// Go-side tools need for content processing — source/target text with
200+
// inline markup, properties, and identity. The heavy fields (skeleton,
201+
// display hints, is_referent) stay on the producing side.
202+
//
203+
// Size comparison for a typical XLSX cell:
204+
// BlockMessage: ~500 bytes (skeleton dominates)
205+
// ContentBlock: ~50 bytes (just id + text)
206+
message ContentBlock {
207+
string id = 1;
208+
string name = 2;
209+
string type = 3;
210+
string mime_type = 4;
211+
bool translatable = 5;
212+
repeated SegmentMessage source = 6;
213+
repeated TargetEntry targets = 7;
214+
map<string, string> properties = 8;
215+
bool preserve_whitespace = 9;
216+
map<string, AnnotationEntry> annotations = 10;
217+
DisplayHintMessage display_hint = 11;
218+
// Stand-off overlays (see BlockMessage.overlays).
219+
repeated OverlayMessage overlays = 12;
220+
}
221+
222+
// ────────────────────────────────────────────────────────────────────────────
223+
// Skeleton
224+
// ────────────────────────────────────────────────────────────────────────────
225+
226+
// SkeletonMessage preserves non-translatable structure for reconstruction.
227+
message SkeletonMessage {
228+
int32 strategy = 1; // 0=FragmentBased, 1=Reparse
229+
repeated SkeletonPartMessage parts = 2;
230+
string source_uri = 3;
231+
}
232+
233+
// SkeletonPartMessage is either a text fragment or a resource reference.
234+
message SkeletonPartMessage {
235+
string text = 1; // Literal text (set for text parts)
236+
string resource_id = 2; // Resource ID (set for reference parts)
237+
string property = 3; // Property to reference (e.g., "target", "source")
238+
string locale = 4; // Target locale for locale-specific references
239+
}
240+
241+
// ────────────────────────────────────────────────────────────────────────────
242+
// Display Hints
243+
// ────────────────────────────────────────────────────────────────────────────
244+
245+
// DisplayHintMessage provides rendering guidance for UI tools.
246+
message DisplayHintMessage {
247+
int32 max_length = 1; // Maximum character count (0 = unlimited)
248+
string content_type = 2; // Content type hint (e.g., "heading", "button")
249+
string context = 3; // Description of where this block appears
250+
string preview = 4; // Short preview of surrounding context
251+
}
252+
253+
// ────────────────────────────────────────────────────────────────────────────
254+
// Content Units
255+
// ────────────────────────────────────────────────────────────────────────────
256+
257+
// BlockMessage represents a translatable content unit.
258+
message BlockMessage {
259+
string id = 1;
260+
string name = 2;
261+
string type = 3;
262+
string mime_type = 4;
263+
bool translatable = 5;
264+
repeated SegmentMessage source = 6;
265+
repeated TargetEntry targets = 7;
266+
map<string, string> properties = 8;
267+
map<string, AnnotationEntry> annotations = 9;
268+
DisplayHintMessage display_hint = 10;
269+
SkeletonMessage skeleton = 11;
270+
bool preserve_whitespace = 12;
271+
bool is_referent = 13;
272+
// Stand-off overlays (term, entity, qa, alignment, plugin types).
273+
// Segmentation is reconstructed from source/targets, so it is not repeated here.
274+
repeated OverlayMessage overlays = 14;
275+
}
276+
277+
// LayerMessage represents a structural layer.
278+
message LayerMessage {
279+
string id = 1;
280+
string name = 2;
281+
string format = 3;
282+
string locale = 4;
283+
string encoding = 5;
284+
string mime_type = 6;
285+
string line_break = 7;
286+
bool is_multilingual = 8;
287+
string parent_id = 9;
288+
map<string, string> properties = 10;
289+
bool has_bom = 11;
290+
}
291+
292+
// DataMessage represents non-translatable document structure.
293+
message DataMessage {
294+
string id = 1;
295+
string name = 2;
296+
map<string, string> properties = 3;
297+
SkeletonMessage skeleton = 4;
298+
bool is_referent = 5;
299+
}
300+
301+
// GroupStartMessage signals the beginning of a structural group.
302+
message GroupStartMessage {
303+
string id = 1;
304+
string name = 2;
305+
string type = 3;
306+
map<string, string> properties = 4;
307+
}
308+
309+
// GroupEndMessage signals the end of a structural group.
310+
message GroupEndMessage {
311+
string id = 1;
312+
}
313+
314+
// MediaMessage holds binary or media content.
315+
message MediaMessage {
316+
string id = 1;
317+
string mime_type = 2;
318+
bytes data = 3;
319+
string uri = 4;
320+
string alt_text = 5;
321+
map<string, string> properties = 6;
322+
}
323+
324+
// PartMessage is the fundamental unit flowing through a stream.
325+
message PartMessage {
326+
int32 part_type = 1;
327+
// Only one of these is set, based on part_type.
328+
BlockMessage block = 2;
329+
LayerMessage layer = 3;
330+
DataMessage data = 4;
331+
GroupStartMessage group_start = 5;
332+
GroupEndMessage group_end = 6;
333+
MediaMessage media = 7;
334+
}
335+
336+
// ────────────────────────────────────────────────────────────────────────────
337+
// Content references
338+
// ────────────────────────────────────────────────────────────────────────────
339+
340+
// ContentRef allows passing content as a file path instead of inline bytes.
341+
message ContentRef {
342+
oneof location {
343+
bytes inline = 1; // Inline bytes
344+
string path = 2; // Local filesystem path
345+
string uri = 3; // Remote/local URI (file://, s3://, gs://)
346+
}
347+
}

0 commit comments

Comments
 (0)