maquina_stream 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/.rdoc_options +30 -0
  3. data/CHANGELOG.md +38 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +163 -0
  6. data/app/assets/stylesheets/maquina_stream/components/attachment.css +35 -0
  7. data/app/assets/stylesheets/maquina_stream/components/code_block.css +30 -0
  8. data/app/assets/stylesheets/maquina_stream/components/shimmer.css +31 -0
  9. data/app/assets/stylesheets/maquina_stream/components/snippet.css +22 -0
  10. data/app/assets/stylesheets/maquina_stream/components/source_citation.css +14 -0
  11. data/app/assets/stylesheets/maquina_stream/components/suggestion.css +32 -0
  12. data/app/assets/stylesheets/maquina_stream/reveal.css +48 -0
  13. data/app/assets/stylesheets/maquina_stream/themes/dark.css +235 -0
  14. data/app/assets/stylesheets/maquina_stream/themes/light.css +117 -0
  15. data/app/controllers/maquina_stream/application_controller.rb +21 -0
  16. data/app/controllers/maquina_stream/blocks_controller.rb +41 -0
  17. data/app/controllers/maquina_stream/manifests_controller.rb +15 -0
  18. data/app/helpers/maquina_stream/components_helper.rb +80 -0
  19. data/app/javascript/maquina_stream/controllers/application_controller.js +169 -0
  20. data/app/javascript/maquina_stream/controllers/ms_autoscroll_controller.js +110 -0
  21. data/app/javascript/maquina_stream/controllers/ms_code_controller.js +96 -0
  22. data/app/javascript/maquina_stream/controllers/ms_deferred_controller.js +223 -0
  23. data/app/javascript/maquina_stream/controllers/ms_diagram_controller.js +40 -0
  24. data/app/javascript/maquina_stream/controllers/ms_link_safety_controller.js +196 -0
  25. data/app/javascript/maquina_stream/controllers/ms_math_controller.js +32 -0
  26. data/app/javascript/maquina_stream/controllers/ms_repair_controller.js +167 -0
  27. data/app/javascript/maquina_stream/controllers/ms_reveal_controller.js +320 -0
  28. data/app/javascript/maquina_stream/controllers/ms_table_controller.js +183 -0
  29. data/app/javascript/maquina_stream/index.js +59 -0
  30. data/app/views/maquina_stream/components/_attachment.html.erb +139 -0
  31. data/app/views/maquina_stream/components/_code_block.html.erb +74 -0
  32. data/app/views/maquina_stream/components/_shimmer.html.erb +36 -0
  33. data/app/views/maquina_stream/components/_snippet.html.erb +50 -0
  34. data/app/views/maquina_stream/components/_source_citation.html.erb +42 -0
  35. data/app/views/maquina_stream/components/_suggestion.html.erb +73 -0
  36. data/config/importmap.rb +10 -0
  37. data/config/locales/en.yml +79 -0
  38. data/config/locales/es.yml +82 -0
  39. data/config/routes.rb +11 -0
  40. data/docs/configuration.md +219 -0
  41. data/docs/deferred-renderers.md +184 -0
  42. data/docs/getting-started.md +356 -0
  43. data/docs/javascript.md +298 -0
  44. data/docs/registries.md +283 -0
  45. data/docs/repair.md +162 -0
  46. data/docs/security.md +247 -0
  47. data/docs/streaming.md +308 -0
  48. data/lib/generators/maquina_stream/install/USAGE +26 -0
  49. data/lib/generators/maquina_stream/install/install_generator.rb +199 -0
  50. data/lib/generators/maquina_stream/install/templates/initializer.rb.tt +121 -0
  51. data/lib/generators/maquina_stream/streamable/USAGE +28 -0
  52. data/lib/generators/maquina_stream/streamable/streamable_generator.rb +187 -0
  53. data/lib/generators/maquina_stream/streamable/templates/migration.rb.tt +21 -0
  54. data/lib/generators/maquina_stream/streamable/templates/model.rb.tt +4 -0
  55. data/lib/maquina_stream/block.rb +99 -0
  56. data/lib/maquina_stream/broadcaster.rb +233 -0
  57. data/lib/maquina_stream/component_cache.rb +0 -0
  58. data/lib/maquina_stream/components/contract.rb +184 -0
  59. data/lib/maquina_stream/components.rb +135 -0
  60. data/lib/maquina_stream/configuration.rb +240 -0
  61. data/lib/maquina_stream/document.rb +296 -0
  62. data/lib/maquina_stream/engine.rb +46 -0
  63. data/lib/maquina_stream/errors.rb +17 -0
  64. data/lib/maquina_stream/export.rb +66 -0
  65. data/lib/maquina_stream/frame.rb +73 -0
  66. data/lib/maquina_stream/manifest.rb +137 -0
  67. data/lib/maquina_stream/registries.rb +116 -0
  68. data/lib/maquina_stream/renderer/fence.rb +115 -0
  69. data/lib/maquina_stream/renderer/post_pass.rb +363 -0
  70. data/lib/maquina_stream/renderer/tag_blocks.rb +276 -0
  71. data/lib/maquina_stream/renderer/view_context.rb +72 -0
  72. data/lib/maquina_stream/renderer.rb +128 -0
  73. data/lib/maquina_stream/sanitizer.rb +392 -0
  74. data/lib/maquina_stream/streamable.rb +281 -0
  75. data/lib/maquina_stream/text_direction.rb +56 -0
  76. data/lib/maquina_stream/themes.rb +84 -0
  77. data/lib/maquina_stream/version.rb +5 -0
  78. data/lib/maquina_stream.rb +175 -0
  79. metadata +204 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MaquinaStream
4
+ # Which way a piece of text reads.
5
+ #
6
+ # A message is one document but not necessarily one direction: an assistant
7
+ # answering in Arabic quotes an English identifier, a Hebrew paragraph sits
8
+ # next to a Ruby fence. Direction is therefore decided per block, the way the
9
+ # bidi algorithm decides it — by the FIRST STRONG character — and not by the
10
+ # document, the locale or the host.
11
+ #
12
+ # `dir="auto"` on every block would let the browser do exactly this, at the
13
+ # cost of eleven bytes on every block of every frame for the majority of
14
+ # documents that are left-to-right and need nothing. So the strong character
15
+ # is found here and the attribute is emitted only when it changes something.
16
+ #
17
+ # The render pipeline calls this per block. A host calls it when it renders
18
+ # text of its own — a message header, an attachment filename — that came from
19
+ # the same place the buffer did.
20
+ module TextDirection
21
+ # The scripts written right to left, as Ruby knows them.
22
+ RTL = /[\p{Hebrew}\p{Arabic}\p{Syriac}\p{Thaana}\p{Nko}\p{Samaritan}\p{Mandaic}\p{Adlam}]/
23
+
24
+ # Only a letter is strong. Digits, punctuation and whitespace take their
25
+ # direction from what surrounds them, which is why "١٢٣ Ruby" reads
26
+ # left-to-right and "مرحبا 123" does not.
27
+ STRONG = /[\p{L}\p{Nl}]/
28
+
29
+ # Explicit bidi controls. They are what a Trojan Source attack is made of:
30
+ # an override inside a comment reorders the rendering of code that the
31
+ # parser reads in a different order entirely.
32
+ CONTROLS = /[\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069]/
33
+
34
+ # The direction `text` reads in: `:rtl`, `:ltr`, or nil when nothing in it
35
+ # is strong either way.
36
+ #
37
+ # Nil is a real answer, not a failure — a block of digits or punctuation
38
+ # has no direction of its own, and stamping one on it would be a guess. The
39
+ # caller emits no `dir` attribute for it.
40
+ def self.of(text)
41
+ strong = text.to_s[STRONG]
42
+ return nil unless strong
43
+
44
+ strong.match?(RTL) ? :rtl : :ltr
45
+ end
46
+
47
+ # Whether `text` contains an explicit bidi control character.
48
+ #
49
+ # True is a reason to be suspicious, not to render differently: see
50
+ # CONTROLS. Code that displays a filename or a link target unescaped should
51
+ # check this first.
52
+ def self.controls?(text)
53
+ text.to_s.match?(CONTROLS)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rouge"
4
+
5
+ module MaquinaStream
6
+ # Two stylesheets, one per scheme, generated from the Rouge themes named in
7
+ # the configuration.
8
+ #
9
+ # Highlighting emits classes and never inline colour, so switching to dark
10
+ # mode is a stylesheet concern and needs no re-render — which matters, because
11
+ # a re-render mid-stream would mean re-broadcasting sealed blocks to change a
12
+ # colour.
13
+ #
14
+ # The two files are generated, not written: `rake maquina_stream:themes`
15
+ # rewrites them from `config.themes`. A host that changes those theme names
16
+ # re-runs that task.
17
+ module Themes
18
+ # Every generated rule is scoped to a code block, so the themes cannot
19
+ # reach anything else on a host's page.
20
+ SCOPE = "[data-ms-code]"
21
+
22
+ # An explicitly chosen dark theme.
23
+ DARK_SELECTOR = '[data-theme="dark"]'
24
+
25
+ # An explicitly chosen light theme, and the default.
26
+ LIGHT_SELECTOR = ':root:not([data-theme="dark"])'
27
+
28
+ # Raised when `config.themes` names a theme Rouge does not define. Raising
29
+ # rather than falling back silently is how the `github_dark` typo in the
30
+ # original default was found.
31
+ class UnknownTheme < Error; end
32
+
33
+ class << self
34
+ # The generated CSS for one scheme, as a String.
35
+ #
36
+ # `scheme` is `:light` or `:dark`; anything else raises ArgumentError.
37
+ # An unknown Rouge theme name raises UnknownTheme.
38
+ def stylesheet(scheme, config: MaquinaStream.config)
39
+ case scheme.to_sym
40
+ when :light then light(config)
41
+ when :dark then dark(config)
42
+ else raise ArgumentError, "scheme must be :light or :dark"
43
+ end
44
+ end
45
+
46
+ # Where the generated stylesheet for `scheme` lives on disk. What the
47
+ # rake task writes to.
48
+ def path(scheme)
49
+ File.expand_path("../../app/assets/stylesheets/maquina_stream/themes/#{scheme}.css", __dir__)
50
+ end
51
+
52
+ private
53
+ def light(config)
54
+ <<~CSS
55
+ /* Generated by `rake maquina_stream:themes`. Do not edit by hand. */
56
+ #{render(config.themes.fetch(:light), "#{LIGHT_SELECTOR} #{SCOPE}")}
57
+ CSS
58
+ end
59
+
60
+ # The dark rules are written twice on purpose: once behind the media
61
+ # query for a viewer who never chose, and once behind the explicit
62
+ # attribute so a chosen theme beats the system preference.
63
+ def dark(config)
64
+ rules = render(config.themes.fetch(:dark), "#{DARK_SELECTOR} #{SCOPE}")
65
+ system = render(config.themes.fetch(:dark), ":root:not([data-theme=\"light\"]) #{SCOPE}")
66
+
67
+ <<~CSS
68
+ /* Generated by `rake maquina_stream:themes`. Do not edit by hand. */
69
+ #{rules}
70
+ @media (prefers-color-scheme: dark) {
71
+ #{system}
72
+ }
73
+ CSS
74
+ end
75
+
76
+ def render(name, scope)
77
+ theme = Rouge::Theme.find(name.to_s)
78
+ raise UnknownTheme, "Rouge has no theme named #{name.inspect}" unless theme
79
+
80
+ theme.render(scope: scope)
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MaquinaStream
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "maquina_stream/version"
4
+ require "maquina_stream/errors"
5
+ require "maquina_stream/configuration"
6
+ require "maquina_stream/registries"
7
+ require "maquina_stream/streamable"
8
+ require "maquina_stream/renderer"
9
+ require "maquina_stream/document"
10
+ require "maquina_stream/block"
11
+ require "maquina_stream/broadcaster"
12
+ require "maquina_stream/frame"
13
+ require "maquina_stream/manifest"
14
+ require "maquina_stream/sanitizer"
15
+ require "maquina_stream/text_direction"
16
+ require "maquina_stream/themes"
17
+ require "maquina_stream/export"
18
+ require "maquina_stream/component_cache"
19
+ require "maquina_stream/components"
20
+ require "maquina_stream/components/contract"
21
+ require "maquina_stream/engine" if defined?(Rails::Engine)
22
+
23
+ # Server-rendered streaming markdown for Rails, over Turbo, with a repair path.
24
+ #
25
+ # A model writes markdown a token at a time. The engine renders it on the
26
+ # server, broadcasts small patches as it grows, and reconciles the browser with
27
+ # the truth when frames go missing — which they do, because Action Cable offers
28
+ # no delivery guarantee. **Only rendered HTML reaches the browser**; the client
29
+ # never parses markdown.
30
+ #
31
+ # ## The whole integration
32
+ #
33
+ # ```ruby
34
+ # class Message < ApplicationRecord
35
+ # include MaquinaStream::Streamable
36
+ #
37
+ # maquina_stream buffer: :content,
38
+ # stream_for: ->(m) { [m.conversation, :messages] }
39
+ # end
40
+ #
41
+ # broadcaster = MaquinaStream::Broadcaster.new(message)
42
+ # model.stream { |token| broadcaster.append(token) }
43
+ # broadcaster.seal!
44
+ # ```
45
+ #
46
+ # ```erb
47
+ # <%= MaquinaStream.render(message) %>
48
+ # ```
49
+ #
50
+ # ## What a host owns
51
+ #
52
+ # The engine resolves nothing and authorizes nothing on its own. Three things
53
+ # are the host's, and none of them has a default the engine could guess:
54
+ #
55
+ # | Host responsibility | Where it goes |
56
+ # |---|---|
57
+ # | Looking a record up by its stream id | Configuration#find_stream |
58
+ # | Deciding whether a request may see it | Configuration#authorize |
59
+ # | Persisting the buffer, sequence and status | MaquinaStream::Streamable |
60
+ #
61
+ # **With no `authorize` configured, every repair request is refused.** That is
62
+ # deliberate: an engine that guesses is an engine that leaks.
63
+ #
64
+ # ## Where to look next
65
+ #
66
+ # | Object | Responsibility |
67
+ # |---|---|
68
+ # | MaquinaStream::Configuration | Every option, its default and what changing it costs |
69
+ # | MaquinaStream::Streamable | The host contract, and the macro that generates it |
70
+ # | MaquinaStream::Broadcaster | Frame coalescing and Turbo Stream emission |
71
+ # | MaquinaStream::Renderer | Markdown in, sanitized HTML out. A pure function |
72
+ # | MaquinaStream::Document | Splits a rendered message into blocks and decides which may freeze |
73
+ # | MaquinaStream::Block | One top-level block: id, markdown, HTML, digest |
74
+ # | MaquinaStream::Frame | One broadcast: what changed since the last one |
75
+ # | MaquinaStream::Manifest | What the browser is told the message currently is |
76
+ # | MaquinaStream::Sanitizer | Allowlist plus URL hardening, the last pass before output |
77
+ # | MaquinaStream::Export | A whole message, back out as markdown |
78
+ # | MaquinaStream::TextDirection | Which way a piece of text reads |
79
+ # | MaquinaStream::Components::Contract | The engine's half of a vendored component |
80
+ #
81
+ # Longer-form documentation ships in `docs/`: `getting-started.md`,
82
+ # `configuration.md`, `streaming.md`, `repair.md`, `registries.md`,
83
+ # `javascript.md`, `security.md` and `deferred-renderers.md`.
84
+ module MaquinaStream
85
+ extend Registries
86
+
87
+ # Components destined for maquina_components, vendored inside the engine for
88
+ # now. Everything renders through MaquinaStream::Components, so extraction is
89
+ # a matter of publishing the partial there and dropping the name from here.
90
+ VENDORED_COMPONENTS = %i[attachment code_block suggestion snippet].freeze
91
+
92
+ class << self
93
+ # The current Configuration. Memoized; the same object `configure`
94
+ # yields.
95
+ def config
96
+ @config ||= Configuration.new
97
+ end
98
+
99
+ # Configures the engine. Yields the Configuration and returns it.
100
+ #
101
+ # ```ruby
102
+ # MaquinaStream.configure do |c|
103
+ # c.find_stream = ->(sid) { Message.find_by(id: sid) }
104
+ # c.authorize = ->(record, request) { record.conversation.readable_by?(request) }
105
+ # end
106
+ # ```
107
+ #
108
+ # Every option, with its default and the consequence of changing it, is
109
+ # documented on Configuration. Call this once from an initializer:
110
+ # configuration is global and read on every render, so changing it
111
+ # mid-stream changes what later frames of an open message look like.
112
+ def configure
113
+ yield config
114
+ config
115
+ end
116
+
117
+ # Throws away the configuration and starts again from the defaults.
118
+ #
119
+ # For tests. A host that calls this in production loses its `find_stream`
120
+ # and `authorize` seams, and every repair request after it is refused.
121
+ def reset_configuration!
122
+ @config = Configuration.new
123
+ end
124
+
125
+ # Renders a whole message to HTML, cached once it is sealed. Returns an
126
+ # `html_safe` String.
127
+ #
128
+ # ```erb
129
+ # <%= MaquinaStream.render(message) %>
130
+ # ```
131
+ #
132
+ # This is the history path — one call, one finished message on a page. It
133
+ # is not what a live stream uses: an open stream goes out frame by frame
134
+ # through Broadcaster. Pagination stays the host's; the engine renders the
135
+ # messages the host chose, in the order it chose them.
136
+ #
137
+ # `record` must satisfy MaquinaStream::Streamable. `config:` defaults to
138
+ # the global MaquinaStream.config.
139
+ #
140
+ # A sealed message is immutable, so its HTML is a pure function of its
141
+ # buffer and can be cached by digest — which is what makes a page of history
142
+ # cheap: re-rendering fifty finished messages on every page load is work
143
+ # nobody asked for. An open message is never cached; it is about to change.
144
+ #
145
+ # The digest is of the buffer, so a host that edits a message gets a new key
146
+ # rather than a stale render.
147
+ def render(record, config: self.config)
148
+ markdown = record.maquina_stream_buffer
149
+
150
+ # Through Document, not Renderer. Renderer produces the HTML; Document is
151
+ # what stamps each block with the id and digest the DOM contract requires,
152
+ # and without those a page cannot be repaired at all — ms-repair would
153
+ # have nothing to compare a manifest against.
154
+ return document_html(record, markdown, config) if record.maquina_stream_open?
155
+
156
+ ComponentCache.fetch("document", record.maquina_stream_id, Digest::SHA256.hexdigest(markdown.to_s)) do
157
+ document_html(record, markdown, config)
158
+ end
159
+ end
160
+
161
+ private
162
+
163
+ def document_html(record, markdown, config)
164
+ blocks = Document.new(
165
+ markdown,
166
+ config: config,
167
+ sid: record.maquina_stream_id,
168
+ mode: record.maquina_stream_open? ? :streaming : :static
169
+ ).blocks
170
+
171
+ html = blocks.map(&:html).join("\n")
172
+ html.respond_to?(:html_safe) ? html.html_safe : html
173
+ end
174
+ end
175
+ end
metadata ADDED
@@ -0,0 +1,204 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maquina_stream
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Mario Alberto Chávez
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '8.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '8.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: maquina_remend
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0.1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: commonmarker
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: nokogiri
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rouge
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ description: Rails engine that renders a streaming markdown buffer to HTML on the
83
+ server and broadcasts it over Turbo Streams. Only rendered HTML reaches the browser.
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files:
87
+ - CHANGELOG.md
88
+ - LICENSE.txt
89
+ - README.md
90
+ files:
91
+ - ".rdoc_options"
92
+ - CHANGELOG.md
93
+ - LICENSE.txt
94
+ - README.md
95
+ - app/assets/stylesheets/maquina_stream/components/attachment.css
96
+ - app/assets/stylesheets/maquina_stream/components/code_block.css
97
+ - app/assets/stylesheets/maquina_stream/components/shimmer.css
98
+ - app/assets/stylesheets/maquina_stream/components/snippet.css
99
+ - app/assets/stylesheets/maquina_stream/components/source_citation.css
100
+ - app/assets/stylesheets/maquina_stream/components/suggestion.css
101
+ - app/assets/stylesheets/maquina_stream/reveal.css
102
+ - app/assets/stylesheets/maquina_stream/themes/dark.css
103
+ - app/assets/stylesheets/maquina_stream/themes/light.css
104
+ - app/controllers/maquina_stream/application_controller.rb
105
+ - app/controllers/maquina_stream/blocks_controller.rb
106
+ - app/controllers/maquina_stream/manifests_controller.rb
107
+ - app/helpers/maquina_stream/components_helper.rb
108
+ - app/javascript/maquina_stream/controllers/application_controller.js
109
+ - app/javascript/maquina_stream/controllers/ms_autoscroll_controller.js
110
+ - app/javascript/maquina_stream/controllers/ms_code_controller.js
111
+ - app/javascript/maquina_stream/controllers/ms_deferred_controller.js
112
+ - app/javascript/maquina_stream/controllers/ms_diagram_controller.js
113
+ - app/javascript/maquina_stream/controllers/ms_link_safety_controller.js
114
+ - app/javascript/maquina_stream/controllers/ms_math_controller.js
115
+ - app/javascript/maquina_stream/controllers/ms_repair_controller.js
116
+ - app/javascript/maquina_stream/controllers/ms_reveal_controller.js
117
+ - app/javascript/maquina_stream/controllers/ms_table_controller.js
118
+ - app/javascript/maquina_stream/index.js
119
+ - app/views/maquina_stream/components/_attachment.html.erb
120
+ - app/views/maquina_stream/components/_code_block.html.erb
121
+ - app/views/maquina_stream/components/_shimmer.html.erb
122
+ - app/views/maquina_stream/components/_snippet.html.erb
123
+ - app/views/maquina_stream/components/_source_citation.html.erb
124
+ - app/views/maquina_stream/components/_suggestion.html.erb
125
+ - config/importmap.rb
126
+ - config/locales/en.yml
127
+ - config/locales/es.yml
128
+ - config/routes.rb
129
+ - docs/configuration.md
130
+ - docs/deferred-renderers.md
131
+ - docs/getting-started.md
132
+ - docs/javascript.md
133
+ - docs/registries.md
134
+ - docs/repair.md
135
+ - docs/security.md
136
+ - docs/streaming.md
137
+ - lib/generators/maquina_stream/install/USAGE
138
+ - lib/generators/maquina_stream/install/install_generator.rb
139
+ - lib/generators/maquina_stream/install/templates/initializer.rb.tt
140
+ - lib/generators/maquina_stream/streamable/USAGE
141
+ - lib/generators/maquina_stream/streamable/streamable_generator.rb
142
+ - lib/generators/maquina_stream/streamable/templates/migration.rb.tt
143
+ - lib/generators/maquina_stream/streamable/templates/model.rb.tt
144
+ - lib/maquina_stream.rb
145
+ - lib/maquina_stream/block.rb
146
+ - lib/maquina_stream/broadcaster.rb
147
+ - lib/maquina_stream/component_cache.rb
148
+ - lib/maquina_stream/components.rb
149
+ - lib/maquina_stream/components/contract.rb
150
+ - lib/maquina_stream/configuration.rb
151
+ - lib/maquina_stream/document.rb
152
+ - lib/maquina_stream/engine.rb
153
+ - lib/maquina_stream/errors.rb
154
+ - lib/maquina_stream/export.rb
155
+ - lib/maquina_stream/frame.rb
156
+ - lib/maquina_stream/manifest.rb
157
+ - lib/maquina_stream/registries.rb
158
+ - lib/maquina_stream/renderer.rb
159
+ - lib/maquina_stream/renderer/fence.rb
160
+ - lib/maquina_stream/renderer/post_pass.rb
161
+ - lib/maquina_stream/renderer/tag_blocks.rb
162
+ - lib/maquina_stream/renderer/view_context.rb
163
+ - lib/maquina_stream/sanitizer.rb
164
+ - lib/maquina_stream/streamable.rb
165
+ - lib/maquina_stream/text_direction.rb
166
+ - lib/maquina_stream/themes.rb
167
+ - lib/maquina_stream/version.rb
168
+ homepage: https://github.com/maquina-app/maquina_stream
169
+ licenses:
170
+ - MIT
171
+ metadata:
172
+ homepage_uri: https://maquina.app
173
+ source_code_uri: https://github.com/maquina-app/maquina_stream
174
+ changelog_uri: https://github.com/maquina-app/maquina_stream/blob/main/CHANGELOG.md
175
+ documentation_uri: https://rubydoc.info/gems/maquina_stream/0.1.0
176
+ rubygems_mfa_required: 'true'
177
+ rdoc_options:
178
+ - "--markup"
179
+ - markdown
180
+ - "--main"
181
+ - README.md
182
+ - "--title"
183
+ - maquina_stream 0.1.0
184
+ - "--exclude"
185
+ - "(?:\\A|/)test/"
186
+ - "--exclude"
187
+ - "(?:\\A|/)sdd/"
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '3.3'
195
+ required_rubygems_version: !ruby/object:Gem::Requirement
196
+ requirements:
197
+ - - ">="
198
+ - !ruby/object:Gem::Version
199
+ version: '0'
200
+ requirements: []
201
+ rubygems_version: 4.0.17
202
+ specification_version: 4
203
+ summary: Streaming markdown rendering for Rails, server-side.
204
+ test_files: []