Class: Rubino::UI::PasteStore
- Inherits:
-
Object
- Object
- Rubino::UI::PasteStore
- Defined in:
- lib/rubino/ui/paste_store.rb
Overview
The per-session PASTE store behind the composer’s file-backed paste pipeline (Hermes-style, two tiers).
A large bracketed paste does not flood the composer: the body is registered here and a single compact PLACEHOLDER token —“[Pasted text #1 +123 lines]” — is inserted into the editable buffer instead. The token rides the draft like normal text (editable around, history-recalled, queueable) and is EXPANDED to the full body only at the message-build seam, where the line leaves the composer for the agent loop (ChatCommand#run_turn): the model sees everything, while the transcript echo keeps the placeholder so scrollback stays clean.
Two tiers, both behind one placeholder shape:
* Tier 1 — PLACEHOLDER COLLAPSE: a paste longer than
`paste.collapse_lines` lines (default 5) is held in memory and the
token expands to the verbatim body at submit.
* Tier 2 — FILE OVERFLOW: a paste bigger than
`paste.file_threshold_tokens` (default 8000, estimated at the same
chars/4 rule Context::TokenBudget uses) is written to a session-
scoped file — <RUBINO_HOME>/sessions/<id>/paste_N.txt — and the
token expands to a one-line pointer telling the model to read the
file with the read tool. The home sessions dir is where session
artifacts already live, it never pollutes the workspace tree, and
the read tool is deliberately un-sandboxed (only WRITES are gated
by Workspace roots), so the model can read it from any cwd.
Lifecycle: a tier-1 body is consumed when its token is expanded into an outgoing message (re-submitting the line from history later leaves the literal placeholder, matching Hermes); tier-2 files persist for the session so the model can re-read them in later turns. Pastes at or under the collapse threshold never reach the store — they inline into the buffer exactly as before.
Constant Summary collapse
- TOKEN_RE =
The placeholder shape, shared with the CompletionSource highlight and the composer’s whole-token backspace.
/\[Pasted text #\d+ \+\d+ lines\]/- DEFAULT_COLLAPSE_LINES =
Built-in fallbacks when config is missing/garbage.
5- DEFAULT_THRESHOLD_TOKENS =
8000
Instance Attribute Summary collapse
-
#session_source ⇒ Object
writeonly
Late wiring for the session scope (see #initialize) — the chat command builds the store before the runner exists.
Instance Method Summary collapse
-
#collapse?(body) ⇒ Boolean
True when
bodyshould collapse to a placeholder instead of inlining: strictly more lines than paste.collapse_lines. -
#expand(text) ⇒ Object
Expands every registered placeholder in
textto its stored body (tier 1) or file pointer (tier 2) — the message-build seam. -
#expansions_in(text) ⇒ Object
The registered [token, body] pairs whose placeholder appears in
text, CONSUMING them like #expand does (re-submitting from history later leaves the literal placeholder). -
#initialize(config: nil, session_source: nil) ⇒ PasteStore
constructor
A new instance of PasteStore.
-
#placeholder_span(buffer, cursor) ⇒ Object
The [start, length] (codepoint) span of the registered placeholder covering the char just BEFORE
cursorinbuffer, or nil. -
#register(body) ⇒ Object
Registers a pasted
bodyand returns the placeholder token to insert into the buffer.
Constructor Details
#initialize(config: nil, session_source: nil) ⇒ PasteStore
Returns a new instance of PasteStore.
56 57 58 59 60 61 |
# File 'lib/rubino/ui/paste_store.rb', line 56 def initialize(config: nil, session_source: nil) @config = config @session_source = session_source @entries = {} # placeholder token => expansion text @counter = 0 end |
Instance Attribute Details
#session_source=(value) ⇒ Object (writeonly)
Late wiring for the session scope (see #initialize) — the chat command builds the store before the runner exists.
65 66 67 |
# File 'lib/rubino/ui/paste_store.rb', line 65 def session_source=(value) @session_source = value end |
Instance Method Details
#collapse?(body) ⇒ Boolean
True when body should collapse to a placeholder instead of inlining: strictly more lines than paste.collapse_lines.
69 70 71 |
# File 'lib/rubino/ui/paste_store.rb', line 69 def collapse?(body) body.to_s.lines.length > collapse_lines end |
#expand(text) ⇒ Object
Expands every registered placeholder in text to its stored body (tier 1) or file pointer (tier 2) — the message-build seam. Consumed entries are dropped (“cleared on submit”); unknown placeholder-shaped text is left verbatim, so user-typed literals are never rewritten.
89 90 91 92 93 |
# File 'lib/rubino/ui/paste_store.rb', line 89 def (text) return text unless text.is_a?(String) && @entries.keys.any? { |t| text.include?(t) } text.gsub(TOKEN_RE) { |token| @entries.delete(token) || token } end |
#expansions_in(text) ⇒ Object
The registered [token, body] pairs whose placeholder appears in text, CONSUMING them like #expand does (re-submitting from history later leaves the literal placeholder). Returned as an array of pairs so the tokens survive JSON round-trips intact (a token is not a valid symbol key). Empty array when text carries no live placeholder.
100 101 102 103 104 105 106 107 |
# File 'lib/rubino/ui/paste_store.rb', line 100 def expansions_in(text) return [] unless text.is_a?(String) text.scan(TOKEN_RE).uniq.filter_map do |token| body = @entries.delete(token) [token, body] if body end end |
#placeholder_span(buffer, cursor) ⇒ Object
The [start, length] (codepoint) span of the registered placeholder covering the char just BEFORE cursor in buffer, or nil. The composer’s backspace uses it to delete a placeholder WHOLE — a half-eaten token would neither read nor expand. Only spans the store actually registered qualify; lookalike text the user typed is edited char-by-char as usual.
115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/rubino/ui/paste_store.rb', line 115 def placeholder_span(buffer, cursor) return nil if @entries.empty? || buffer.nil? pos = 0 while (m = TOKEN_RE.match(buffer, pos)) start = m.begin(0) length = m[0].length return [start, length] if @entries.key?(m[0]) && cursor > start && cursor <= start + length pos = start + length end nil end |
#register(body) ⇒ Object
Registers a pasted body and returns the placeholder token to insert into the buffer. Oversized bodies (tier 2) are written to the session paste file here, at paste time; their token expands to the file pointer instead of the content.
77 78 79 80 81 82 83 |
# File 'lib/rubino/ui/paste_store.rb', line 77 def register(body) body = body.to_s n = (@counter += 1) token = "[Pasted text ##{n} +#{body.lines.length} lines]" @entries[token] = oversize?(body) ? overflow_to_file(n, body) : body token end |