|
| 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