Module: Plushie::Protocol::Encode

Defined in:
lib/plushie/protocol/encode.rb

Overview

Outbound message encoding for the wire protocol.

Every method produces wire-ready bytes (iodata for msgpack, JSONL string for json). The session field defaults to "" and is injected by the Connection or SessionPool when multiplexing.

See Also:

  • "Incoming messages"

Class Method Summary collapse

Class Method Details

.encode(map, format = :msgpack) ⇒ String

Encode an arbitrary hash as wire-format bytes.

Parameters:

  • map (Hash)

    the message hash (symbol keys)

  • format (:msgpack, :json) (defaults to: :msgpack)

    wire format

Returns:

  • (String)

    encoded bytes



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/plushie/protocol/encode.rb', line 23

def encode(map, format = :msgpack)
  case format
  when :json
    JSON.generate(stringify_keys(map)) + "\n"
  when :msgpack
    require "msgpack"
    MessagePack.pack(stringify_keys(map))
  else
    raise ArgumentError, "unknown format: #{format.inspect}"
  end
end

.encode_advance_frame(timestamp, format = :msgpack) ⇒ String

Advance the animation clock by one frame.

Parameters:

  • timestamp (Integer)

    frame timestamp in milliseconds

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


312
313
314
# File 'lib/plushie/protocol/encode.rb', line 312

def encode_advance_frame(timestamp, format = :msgpack)
  encode({type: "advance_frame", session: "", timestamp: timestamp}, format)
end

.encode_binary(data, format) ⇒ String

Encode binary data for the wire format. JSON: base64-encoded string. MessagePack: raw binary pass-through.

Parameters:

  • data (String)

    binary data

  • format (:msgpack, :json)

Returns:

  • (String)


341
342
343
344
345
346
# File 'lib/plushie/protocol/encode.rb', line 341

def encode_binary(data, format)
  case format
  when :json then Base64.strict_encode64(data)
  when :msgpack then data
  end
end

.encode_binary_field(payload, key, format) ⇒ Hash

Encode a binary field in a payload hash if present. Returns a new hash with the field encoded for the wire format, or the original hash if the field is absent or nil.

Parameters:

  • payload (Hash)

    the payload hash

  • key (Symbol)

    the field key to encode

  • format (:msgpack, :json)

Returns:

  • (Hash)


356
357
358
359
# File 'lib/plushie/protocol/encode.rb', line 356

def encode_binary_field(payload, key, format)
  return payload unless payload.is_a?(Hash) && payload.key?(key) && payload[key].is_a?(String)
  payload.merge(key => encode_binary(payload[key], format))
end

.encode_effect(id, kind, payload, format = :msgpack) ⇒ String

Request a platform effect (file dialog, clipboard, notification).

Parameters:

  • id (String)

    unique request ID for correlation

  • kind (String)

    effect kind (e.g. "file_open", "clipboard_read")

  • payload (Hash)

    effect-specific parameters

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


153
154
155
156
157
158
# File 'lib/plushie/protocol/encode.rb', line 153

def encode_effect(id, kind, payload, format = :msgpack)
  encode({
    type: "effect", session: "",
    id: id, kind: kind.to_s, payload: payload
  }, format)
end

.encode_extension_command(node_id, op, payload, format = :msgpack) ⇒ String

Send a command directly to a native widget extension.

Parameters:

  • node_id (String)

    target extension widget ID

  • op (String)

    command name

  • payload (Hash)

    command data

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


201
202
203
204
205
206
# File 'lib/plushie/protocol/encode.rb', line 201

def encode_extension_command(node_id, op, payload, format = :msgpack)
  encode({
    type: "extension_command", session: "",
    node_id: node_id, op: op.to_s, payload: payload
  }, format)
end

.encode_extension_commands(commands, format = :msgpack) ⇒ String

Send multiple extension commands in a single message.

Parameters:

  • commands (Array<Hash>)

    each with :node_id, :op, :payload

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


213
214
215
216
217
218
219
220
# File 'lib/plushie/protocol/encode.rb', line 213

def encode_extension_commands(commands, format = :msgpack)
  encode({
    type: "extension_commands", session: "",
    commands: commands.map { |c|
      {node_id: c[:node_id], op: c[:op].to_s, payload: c[:payload] || {}}
    }
  }, format)
end

.encode_image_op(op, payload, format = :msgpack) ⇒ String

Manage in-memory image handles (create, update, delete).

Binary fields (data, pixels) are base64-encoded for JSON and passed as raw binary for MessagePack.

Parameters:

  • op (String)

    "create_image", "update_image", or "delete_image"

  • payload (Hash)

    includes :handle, and optionally :data or :pixels/:width/:height

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/plushie/protocol/encode.rb', line 173

def encode_image_op(op, payload, format = :msgpack)
  msg = {type: "image_op", session: "", op: op.to_s}
  msg[:handle] = payload[:handle] if payload[:handle]

  if payload[:data]
    msg[:data] = encode_binary(payload[:data], format)
  end

  if payload[:pixels]
    msg[:pixels] = encode_binary(payload[:pixels], format)
    msg[:width] = payload[:width]
    msg[:height] = payload[:height]
  end

  encode(msg, format)
end

.encode_interact(id, action, selector = nil, payload = {}, format = :msgpack) ⇒ String

Simulate a user interaction (click, type, toggle, etc.).

Parameters:

  • id (String)

    request ID for response correlation

  • action (String)

    action name (click, type_text, toggle, etc.)

  • selector (Hash, nil) (defaults to: nil)

    target widget selector

  • payload (Hash) (defaults to: {})

    action-specific parameters

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


252
253
254
255
256
257
258
259
# File 'lib/plushie/protocol/encode.rb', line 252

def encode_interact(id, action, selector = nil, payload = {}, format = :msgpack)
  msg = {
    type: "interact", session: "",
    id: id, action: action.to_s, payload: payload
  }
  msg[:selector] = selector if selector
  encode(msg, format)
end

.encode_patch(ops, format = :msgpack) ⇒ String

Incrementally patch the existing tree.

Parameters:

  • ops (Array<Hash>)

    patch operations

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


75
76
77
# File 'lib/plushie/protocol/encode.rb', line 75

def encode_patch(ops, format = :msgpack)
  encode({type: "patch", session: "", ops: ops}, format)
end

.encode_query(id, target, selector = {}, format = :msgpack) ⇒ String

Query the renderer's tree (find widget, get full tree).

Parameters:

  • id (String)

    request ID for response correlation

  • target (String)

    "find" or "tree"

  • selector (Hash) (defaults to: {})

    selector (e.g. {by: "id", value: "btn1"})

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


233
234
235
236
237
238
# File 'lib/plushie/protocol/encode.rb', line 233

def encode_query(id, target, selector = {}, format = :msgpack)
  encode({
    type: "query", session: "",
    id: id, target: target, selector: selector
  }, format)
end

.encode_reset(id, format = :msgpack) ⇒ String

Reset all session state.

Parameters:

  • id (String)

    request ID

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


299
300
301
# File 'lib/plushie/protocol/encode.rb', line 299

def encode_reset(id, format = :msgpack)
  encode({type: "reset", session: "", id: id}, format)
end

.encode_screenshot(id, name, width = 1024, height = 768, format = :msgpack) ⇒ String

Capture rendered pixels.

Parameters:

  • id (String)

    request ID

  • name (String)

    label for this capture

  • width (Integer) (defaults to: 1024)

    viewport width (default 1024)

  • height (Integer) (defaults to: 768)

    viewport height (default 768)

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


283
284
285
286
287
288
# File 'lib/plushie/protocol/encode.rb', line 283

def encode_screenshot(id, name, width = 1024, height = 768, format = :msgpack)
  encode({
    type: "screenshot", session: "",
    id: id, name: name, width: width, height: height
  }, format)
end

.encode_settings(settings, format = :msgpack) ⇒ String

Encode application-level settings. Sent as the first message.

All fields inside settings are optional. See protocol.md for the full list: protocol_version, default_text_size, default_font, antialiasing, vsync, fonts, scale_factor, validate_props, extension_config, default_event_rate.

Parameters:

  • settings (Hash)

    settings key-value pairs

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


49
50
51
52
53
54
55
# File 'lib/plushie/protocol/encode.rb', line 49

def encode_settings(settings, format = :msgpack)
  encode({
    type: "settings",
    session: "",
    settings: {protocol_version: Protocol::PROTOCOL_VERSION}.merge(settings)
  }, format)
end

.encode_snapshot(tree, format = :msgpack) ⇒ String

Replace the entire UI tree.

Parameters:

  • tree (Hash)

    the tree node (id, type, props, children)

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


66
67
68
# File 'lib/plushie/protocol/encode.rb', line 66

def encode_snapshot(tree, format = :msgpack)
  encode({type: "snapshot", session: "", tree: tree}, format)
end

.encode_subscribe(kind, tag, format = :msgpack, max_rate: nil) ⇒ String

Subscribe to an event category.

Parameters:

  • kind (String, Symbol)

    event category (e.g. "on_key_press")

  • tag (String, Symbol)

    routing tag

  • format (:msgpack, :json) (defaults to: :msgpack)
  • max_rate (Integer, nil) (defaults to: nil)

    max events per second (nil = unlimited)

Returns:

  • (String)


90
91
92
93
94
# File 'lib/plushie/protocol/encode.rb', line 90

def encode_subscribe(kind, tag, format = :msgpack, max_rate: nil)
  msg = {type: "subscribe", session: "", kind: kind.to_s, tag: tag.to_s}
  msg[:max_rate] = max_rate if max_rate
  encode(msg, format)
end

.encode_tree_hash(id, name, format = :msgpack) ⇒ String

Compute a SHA-256 hash of the renderer's current tree.

Parameters:

  • id (String)

    request ID

  • name (String)

    label for this hash capture

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


271
272
273
# File 'lib/plushie/protocol/encode.rb', line 271

def encode_tree_hash(id, name, format = :msgpack)
  encode({type: "tree_hash", session: "", id: id, name: name}, format)
end

.encode_unsubscribe(kind, format = :msgpack) ⇒ String

Unsubscribe from an event category.

Parameters:

  • kind (String, Symbol)

    event category

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


101
102
103
# File 'lib/plushie/protocol/encode.rb', line 101

def encode_unsubscribe(kind, format = :msgpack)
  encode({type: "unsubscribe", session: "", kind: kind.to_s}, format)
end

.encode_widget_op(op, payload, format = :msgpack) ⇒ String

Perform an operation on a widget (focus, scroll, etc.).

Binary fields (e.g. :data for load_font) are automatically base64-encoded for JSON and passed as raw binary for msgpack.

Parameters:

  • op (String, Symbol)

    operation name

  • payload (Hash)

    operation-specific parameters

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


118
119
120
121
122
# File 'lib/plushie/protocol/encode.rb', line 118

def encode_widget_op(op, payload, format = :msgpack)
  # Encode binary fields if present (load_font sends raw TTF/OTF data)
  payload = encode_binary_field(payload, :data, format)
  encode({type: "widget_op", session: "", op: op.to_s, payload: payload}, format)
end

.encode_window_op(op, window_id, settings, format = :msgpack) ⇒ String

Manage a window (open, close, resize, etc.).

Parameters:

  • op (String, Symbol)

    operation name

  • window_id (String)

    target window ID

  • settings (Hash)

    operation-specific parameters

  • format (:msgpack, :json) (defaults to: :msgpack)

Returns:

  • (String)


135
136
137
138
139
140
# File 'lib/plushie/protocol/encode.rb', line 135

def encode_window_op(op, window_id, settings, format = :msgpack)
  encode({
    type: "window_op", session: "",
    op: op.to_s, window_id: window_id, settings: settings
  }, format)
end

.stringify_keys(obj) ⇒ Object

Recursively convert all symbol keys to strings.

Parameters:

  • obj (Object)

    value to stringify

Returns:

  • (Object)

    value with string keys



324
325
326
327
328
329
330
331
332
333
# File 'lib/plushie/protocol/encode.rb', line 324

def stringify_keys(obj)
  case obj
  when Hash
    obj.each_with_object({}) { |(k, v), h| h[k.to_s] = stringify_keys(v) }
  when Array
    obj.map { |v| stringify_keys(v) }
  else
    obj
  end
end