stimulus-pdf-viewer-rails 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE-APACHE +177 -0
- data/LICENSE-MIT +21 -0
- data/NOTICE +27 -0
- data/README.md +221 -0
- data/app/assets/images/stimulus-pdf-viewer/cursor-editorFreeHighlight.svg +6 -0
- data/app/assets/images/stimulus-pdf-viewer/cursor-editorInk.svg +4 -0
- data/app/assets/images/stimulus-pdf-viewer/cursor-editorNote.svg +8 -0
- data/app/assets/images/stimulus-pdf-viewer/cursor-editorTextHighlight.svg +8 -0
- data/app/assets/javascripts/stimulus-pdf-viewer.esm.js +8163 -0
- data/app/assets/javascripts/stimulus-pdf-viewer.js +8191 -0
- data/app/assets/stylesheets/stimulus-pdf-viewer.scss +2843 -0
- data/config/importmap.rb +4 -0
- data/lib/generators/stimulus_pdf_viewer/install/templates/views/_toolbar.html.erb +220 -0
- data/lib/generators/stimulus_pdf_viewer/install/templates/views/_viewer.html.erb +54 -0
- data/lib/generators/stimulus_pdf_viewer/install_generator.rb +95 -0
- data/lib/stimulus-pdf-viewer-rails.rb +2 -0
- data/lib/stimulus_pdf_viewer/rails/engine.rb +26 -0
- data/lib/stimulus_pdf_viewer/rails/version.rb +7 -0
- data/lib/stimulus_pdf_viewer/rails.rb +7 -0
- data/lib/tasks/assets.rake +196 -0
- metadata +89 -0
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
<div class="pdf-toolbar">
|
|
2
|
+
<%# Left section: Sidebar toggle + Page navigation + Zoom %>
|
|
3
|
+
<div class="pdf-toolbar-section pdf-toolbar-left">
|
|
4
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#toggleSidebar" title="Toggle thumbnails">
|
|
5
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
6
|
+
<rect x="3" y="3" width="7" height="18" rx="1" />
|
|
7
|
+
<line x1="14" y1="6" x2="21" y2="6" />
|
|
8
|
+
<line x1="14" y1="12" x2="21" y2="12" />
|
|
9
|
+
<line x1="14" y1="18" x2="21" y2="18" />
|
|
10
|
+
</svg>
|
|
11
|
+
</button>
|
|
12
|
+
|
|
13
|
+
<div class="pdf-toolbar-separator"></div>
|
|
14
|
+
|
|
15
|
+
<div class="pdf-toolbar-group pdf-toolbar-nav">
|
|
16
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#previousPage" title="Previous page" data-pdf-viewer-target="prevBtn">
|
|
17
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
|
18
|
+
<path d="M8 12L3 7l5-5v10z" />
|
|
19
|
+
</svg>
|
|
20
|
+
</button>
|
|
21
|
+
<div class="pdf-page-input-container">
|
|
22
|
+
<input type="number" class="pdf-page-input" data-action="change->pdf-viewer#goToPage keydown->pdf-viewer#handlePageInputKey" data-pdf-viewer-target="pageInput" min="1" value="1" autocomplete="off">
|
|
23
|
+
<span class="pdf-page-separator">/</span>
|
|
24
|
+
<span class="pdf-page-count" data-pdf-viewer-target="pageCount">-</span>
|
|
25
|
+
</div>
|
|
26
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#nextPage" title="Next page" data-pdf-viewer-target="nextBtn">
|
|
27
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor">
|
|
28
|
+
<path d="M8 4l5 5-5 5V4z" />
|
|
29
|
+
</svg>
|
|
30
|
+
</button>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div class="pdf-toolbar-separator"></div>
|
|
34
|
+
|
|
35
|
+
<div class="pdf-toolbar-group pdf-toolbar-zoom">
|
|
36
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#zoomOut" title="Zoom out">
|
|
37
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
38
|
+
<circle cx="11" cy="11" r="8" />
|
|
39
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
40
|
+
<line x1="8" y1="11" x2="14" y2="11" />
|
|
41
|
+
</svg>
|
|
42
|
+
</button>
|
|
43
|
+
<select class="pdf-zoom-select" data-action="change->pdf-viewer#setZoom" data-pdf-viewer-target="zoomSelect">
|
|
44
|
+
<option value="auto" selected>Auto</option>
|
|
45
|
+
<option value="page-width">Page Width</option>
|
|
46
|
+
<option value="page-fit">Page Fit</option>
|
|
47
|
+
<option value="page-actual">Actual Size</option>
|
|
48
|
+
<option disabled>-----</option>
|
|
49
|
+
<option value="0.5">50%</option>
|
|
50
|
+
<option value="0.75">75%</option>
|
|
51
|
+
<option value="1">100%</option>
|
|
52
|
+
<option value="1.25">125%</option>
|
|
53
|
+
<option value="1.5">150%</option>
|
|
54
|
+
<option value="2">200%</option>
|
|
55
|
+
<option value="3">300%</option>
|
|
56
|
+
</select>
|
|
57
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#zoomIn" title="Zoom in">
|
|
58
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
59
|
+
<circle cx="11" cy="11" r="8" />
|
|
60
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
61
|
+
<line x1="11" y1="8" x2="11" y2="14" />
|
|
62
|
+
<line x1="8" y1="11" x2="14" y2="11" />
|
|
63
|
+
</svg>
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<%# Right section: Annotation tools %>
|
|
69
|
+
<div class="pdf-toolbar-section pdf-toolbar-right">
|
|
70
|
+
<div class="pdf-toolbar-group pdf-toolbar-tools">
|
|
71
|
+
<button class="pdf-tool-btn active" data-tool="select" data-action="click->pdf-viewer#selectTool" aria-label="Select tool">
|
|
72
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
73
|
+
<path d="M3 3l7.07 16.97 2.51-7.39 7.39-2.51L3 3z" />
|
|
74
|
+
<path d="M13 13l6 6" />
|
|
75
|
+
</svg>
|
|
76
|
+
<span>Select</span>
|
|
77
|
+
</button>
|
|
78
|
+
<button class="pdf-tool-btn" data-tool="highlight" data-action="click->pdf-viewer#selectTool" aria-label="Highlight tool">
|
|
79
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
80
|
+
<path d="M12 20h9" />
|
|
81
|
+
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z" />
|
|
82
|
+
<rect x="2" y="18" width="8" height="4" rx="1" fill="#FFEB3B" stroke="none" />
|
|
83
|
+
</svg>
|
|
84
|
+
<span>Highlight</span>
|
|
85
|
+
</button>
|
|
86
|
+
<button class="pdf-tool-btn" data-tool="underline" data-action="click->pdf-viewer#selectTool" aria-label="Underline tool">
|
|
87
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
88
|
+
<path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3" />
|
|
89
|
+
<line x1="4" y1="21" x2="20" y2="21" stroke-width="3" />
|
|
90
|
+
</svg>
|
|
91
|
+
<span>Underline</span>
|
|
92
|
+
</button>
|
|
93
|
+
<button class="pdf-tool-btn" data-tool="note" data-action="click->pdf-viewer#selectTool" aria-label="Add note">
|
|
94
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
95
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" fill="#FFF9C4" />
|
|
96
|
+
<line x1="8" y1="9" x2="16" y2="9" />
|
|
97
|
+
<line x1="8" y1="13" x2="14" y2="13" />
|
|
98
|
+
</svg>
|
|
99
|
+
<span>Add Note</span>
|
|
100
|
+
</button>
|
|
101
|
+
<button class="pdf-tool-btn" data-tool="ink" data-action="click->pdf-viewer#selectTool" aria-label="Draw tool">
|
|
102
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
103
|
+
<path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" />
|
|
104
|
+
</svg>
|
|
105
|
+
<span>Draw</span>
|
|
106
|
+
</button>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="pdf-toolbar-separator"></div>
|
|
110
|
+
|
|
111
|
+
<div class="pdf-toolbar-group pdf-toolbar-colors" data-pdf-viewer-target="colorPicker">
|
|
112
|
+
<%# Color picker will be rendered here by JavaScript %>
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<div class="pdf-toolbar-separator"></div>
|
|
116
|
+
|
|
117
|
+
<div class="pdf-toolbar-group pdf-toolbar-actions">
|
|
118
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#toggleSearch" title="Search (Ctrl+F)">
|
|
119
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
120
|
+
<circle cx="11" cy="11" r="8" />
|
|
121
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
122
|
+
</svg>
|
|
123
|
+
</button>
|
|
124
|
+
<div class="pdf-toolbar-separator"></div>
|
|
125
|
+
|
|
126
|
+
<%# Overflow menu button (visible on mobile only) %>
|
|
127
|
+
<button class="pdf-toolbar-overflow-btn" data-action="click->pdf-viewer#toggleOverflowMenu" title="More tools" data-pdf-viewer-target="overflowBtn">
|
|
128
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
129
|
+
<circle cx="12" cy="12" r="1" fill="currentColor" />
|
|
130
|
+
<circle cx="12" cy="5" r="1" fill="currentColor" />
|
|
131
|
+
<circle cx="12" cy="19" r="1" fill="currentColor" />
|
|
132
|
+
</svg>
|
|
133
|
+
</button>
|
|
134
|
+
|
|
135
|
+
<button class="pdf-toolbar-btn" data-action="click->pdf-viewer#toggleAnnotationSidebar" title="Annotations">
|
|
136
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
137
|
+
<line x1="17" y1="6" x2="21" y2="6" />
|
|
138
|
+
<line x1="17" y1="12" x2="21" y2="12" />
|
|
139
|
+
<line x1="17" y1="18" x2="21" y2="18" />
|
|
140
|
+
<rect x="3" y="3" width="7" height="18" rx="1" />
|
|
141
|
+
</svg>
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<%# Overflow menu dropdown (mobile - nav, zoom, actions) %>
|
|
146
|
+
<div class="pdf-toolbar-overflow-menu" data-pdf-viewer-target="overflowMenu">
|
|
147
|
+
<%# Page Navigation %>
|
|
148
|
+
<div class="pdf-overflow-section">
|
|
149
|
+
<div class="pdf-overflow-row">
|
|
150
|
+
<button class="pdf-overflow-btn" data-action="click->pdf-viewer#previousPage" title="Previous page">
|
|
151
|
+
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor">
|
|
152
|
+
<path d="M8 12L3 7l5-5v10z" />
|
|
153
|
+
</svg>
|
|
154
|
+
</button>
|
|
155
|
+
<div class="pdf-overflow-page-display">
|
|
156
|
+
<span data-pdf-viewer-target="overflowPageNum">1</span>
|
|
157
|
+
<span class="pdf-page-separator">/</span>
|
|
158
|
+
<span data-pdf-viewer-target="overflowPageCount">-</span>
|
|
159
|
+
</div>
|
|
160
|
+
<button class="pdf-overflow-btn" data-action="click->pdf-viewer#nextPage" title="Next page">
|
|
161
|
+
<svg width="20" height="20" viewBox="0 0 16 16" fill="currentColor">
|
|
162
|
+
<path d="M8 4l5 5-5 5V4z" />
|
|
163
|
+
</svg>
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
|
|
168
|
+
<%# More Tools (underline, ink) %>
|
|
169
|
+
<div class="pdf-overflow-section">
|
|
170
|
+
<div class="pdf-overflow-section-title">More Tools</div>
|
|
171
|
+
<div class="pdf-overflow-tools">
|
|
172
|
+
<button class="pdf-overflow-tool-btn" data-tool="underline" data-action="click->pdf-viewer#selectToolFromOverflow" aria-label="Underline tool">
|
|
173
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
174
|
+
<path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3" />
|
|
175
|
+
<line x1="4" y1="21" x2="20" y2="21" stroke-width="3" />
|
|
176
|
+
</svg>
|
|
177
|
+
<span>Underline</span>
|
|
178
|
+
</button>
|
|
179
|
+
<button class="pdf-overflow-tool-btn" data-tool="ink" data-action="click->pdf-viewer#selectToolFromOverflow" aria-label="Draw tool">
|
|
180
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
181
|
+
<path d="M17 3a2.828 2.828 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" />
|
|
182
|
+
</svg>
|
|
183
|
+
<span>Draw</span>
|
|
184
|
+
</button>
|
|
185
|
+
</div>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
<%# Actions %>
|
|
189
|
+
<div class="pdf-overflow-section">
|
|
190
|
+
<div class="pdf-overflow-actions">
|
|
191
|
+
<button class="pdf-overflow-action-btn" data-action="click->pdf-viewer#toggleSearch">
|
|
192
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
193
|
+
<circle cx="11" cy="11" r="8" />
|
|
194
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
195
|
+
</svg>
|
|
196
|
+
<span>Search</span>
|
|
197
|
+
</button>
|
|
198
|
+
<button class="pdf-overflow-action-btn" data-action="click->pdf-viewer#toggleSidebar">
|
|
199
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
200
|
+
<rect x="3" y="3" width="7" height="18" rx="1" />
|
|
201
|
+
<line x1="14" y1="6" x2="21" y2="6" />
|
|
202
|
+
<line x1="14" y1="12" x2="21" y2="12" />
|
|
203
|
+
<line x1="14" y1="18" x2="21" y2="18" />
|
|
204
|
+
</svg>
|
|
205
|
+
<span>Thumbnails</span>
|
|
206
|
+
</button>
|
|
207
|
+
<button class="pdf-overflow-action-btn" data-action="click->pdf-viewer#toggleAnnotationSidebar">
|
|
208
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
209
|
+
<line x1="17" y1="6" x2="21" y2="6" />
|
|
210
|
+
<line x1="17" y1="12" x2="21" y2="12" />
|
|
211
|
+
<line x1="17" y1="18" x2="21" y2="18" />
|
|
212
|
+
<rect x="3" y="3" width="7" height="18" rx="1" />
|
|
213
|
+
</svg>
|
|
214
|
+
<span>Annotations</span>
|
|
215
|
+
</button>
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
<%#
|
|
2
|
+
PDF Viewer partial example for Rails
|
|
3
|
+
|
|
4
|
+
Required local variables:
|
|
5
|
+
- document: An object with `file` (ActiveStorage attachment) and `name` attributes
|
|
6
|
+
- annotations_url: Path to the annotations REST endpoint
|
|
7
|
+
|
|
8
|
+
Optional local variables:
|
|
9
|
+
- user_name: Current user's name (for watermarks)
|
|
10
|
+
- organization_name: Organization name (for watermarks)
|
|
11
|
+
- initial_page: Page number to open on load
|
|
12
|
+
- initial_annotation: Annotation ID to highlight on load
|
|
13
|
+
- tracking_url: Endpoint for time tracking (optional)
|
|
14
|
+
%>
|
|
15
|
+
|
|
16
|
+
<div class="pdf-viewer-container"
|
|
17
|
+
data-controller="pdf-viewer"
|
|
18
|
+
data-pdf-viewer-target="container"
|
|
19
|
+
data-pdf-viewer-document-url-value="<%= url_for(document.file) %>"
|
|
20
|
+
data-pdf-viewer-document-name-value="<%= document.name %>"
|
|
21
|
+
data-pdf-viewer-annotations-url-value="<%= annotations_url %>"
|
|
22
|
+
<% if local_assigns[:user_name] %>
|
|
23
|
+
data-pdf-viewer-user-name-value="<%= user_name %>"
|
|
24
|
+
<% end %>
|
|
25
|
+
<% if local_assigns[:organization_name] %>
|
|
26
|
+
data-pdf-viewer-organization-name-value="<%= organization_name %>"
|
|
27
|
+
<% end %>
|
|
28
|
+
<% if local_assigns[:initial_page] %>
|
|
29
|
+
data-pdf-viewer-initial-page-value="<%= initial_page %>"
|
|
30
|
+
<% end %>
|
|
31
|
+
<% if local_assigns[:initial_annotation] %>
|
|
32
|
+
data-pdf-viewer-initial-annotation-value="<%= initial_annotation %>"
|
|
33
|
+
<% end %>
|
|
34
|
+
<% if local_assigns[:tracking_url] %>
|
|
35
|
+
data-pdf-viewer-tracking-url-value="<%= tracking_url %>"
|
|
36
|
+
<% end %>>
|
|
37
|
+
|
|
38
|
+
<%# Toolbar - render the toolbar partial %>
|
|
39
|
+
<div class="pdf-viewer-toolbar">
|
|
40
|
+
<%= render "path/to/toolbar" %>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<%# Main viewer body %>
|
|
44
|
+
<div class="pdf-viewer-body">
|
|
45
|
+
<%# Loading overlay - shown while PDF loads %>
|
|
46
|
+
<div class="pdf-loading-overlay" data-pdf-viewer-target="loadingOverlay">
|
|
47
|
+
<div class="pdf-loading-spinner"></div>
|
|
48
|
+
<div class="pdf-loading-text">Loading document...</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<%# PDF pages container - pages are rendered here by JavaScript %>
|
|
52
|
+
<div class="pdf-pages-container"></div>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
require "rails/generators"
|
|
2
|
+
|
|
3
|
+
module StimulusPdfViewer
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("install/templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Install stimulus-pdf-viewer into your Rails application"
|
|
9
|
+
|
|
10
|
+
def register_stimulus_controller
|
|
11
|
+
say "Registering Stimulus controller..."
|
|
12
|
+
|
|
13
|
+
controller_registration = <<~JS
|
|
14
|
+
|
|
15
|
+
// PDF Viewer
|
|
16
|
+
import { PdfViewerController, PdfDownloadController } from "stimulus-pdf-viewer"
|
|
17
|
+
application.register("pdf-viewer", PdfViewerController)
|
|
18
|
+
application.register("pdf-download", PdfDownloadController)
|
|
19
|
+
JS
|
|
20
|
+
|
|
21
|
+
controllers_file = "app/javascript/controllers/index.js"
|
|
22
|
+
|
|
23
|
+
if File.exist?(controllers_file)
|
|
24
|
+
append_to_file controllers_file, controller_registration
|
|
25
|
+
say "Added controller registration to #{controllers_file}", :green
|
|
26
|
+
else
|
|
27
|
+
say "Could not find #{controllers_file}. Please manually register the controllers:", :yellow
|
|
28
|
+
say controller_registration
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_stylesheet_import
|
|
33
|
+
say "Adding stylesheet import..."
|
|
34
|
+
|
|
35
|
+
stylesheet_import = '@import "stimulus-pdf-viewer";'
|
|
36
|
+
|
|
37
|
+
# Try common stylesheet locations
|
|
38
|
+
stylesheet_files = [
|
|
39
|
+
"app/assets/stylesheets/application.scss",
|
|
40
|
+
"app/assets/stylesheets/application.css.scss",
|
|
41
|
+
"app/assets/stylesheets/application.sass.scss"
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
stylesheet_file = stylesheet_files.find { |f| File.exist?(f) }
|
|
45
|
+
|
|
46
|
+
if stylesheet_file
|
|
47
|
+
append_to_file stylesheet_file, "\n#{stylesheet_import}\n"
|
|
48
|
+
say "Added stylesheet import to #{stylesheet_file}", :green
|
|
49
|
+
else
|
|
50
|
+
say "Could not find a SCSS stylesheet. Please manually add:", :yellow
|
|
51
|
+
say stylesheet_import
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def add_pdf_worker_meta_tag
|
|
56
|
+
say "Adding PDF.js worker meta tag..."
|
|
57
|
+
|
|
58
|
+
meta_tag = '<meta name="pdf-worker-src" content="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.js">'
|
|
59
|
+
|
|
60
|
+
layout_file = "app/views/layouts/application.html.erb"
|
|
61
|
+
|
|
62
|
+
if File.exist?(layout_file)
|
|
63
|
+
inject_into_file layout_file, " #{meta_tag}\n", after: "<head>\n"
|
|
64
|
+
say "Added PDF worker meta tag to #{layout_file}", :green
|
|
65
|
+
else
|
|
66
|
+
say "Could not find #{layout_file}. Please manually add to your <head>:", :yellow
|
|
67
|
+
say meta_tag
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def copy_example_partials
|
|
72
|
+
if yes?("Would you like to copy example view partials? (y/n)")
|
|
73
|
+
directory "views", "app/views/pdf_viewer"
|
|
74
|
+
say "Copied example partials to app/views/pdf_viewer/", :green
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def show_next_steps
|
|
79
|
+
say ""
|
|
80
|
+
say "=" * 60, :green
|
|
81
|
+
say "stimulus-pdf-viewer installed successfully!", :green
|
|
82
|
+
say "=" * 60, :green
|
|
83
|
+
say ""
|
|
84
|
+
say "Next steps:"
|
|
85
|
+
say "1. Create an Annotation model and controller for your app"
|
|
86
|
+
say "2. Set up routes for annotations REST API"
|
|
87
|
+
say "3. Add the PDF viewer partial to your views"
|
|
88
|
+
say ""
|
|
89
|
+
say "See the README for detailed integration instructions:"
|
|
90
|
+
say "https://github.com/jhubert/stimulus-pdf-viewer-rails"
|
|
91
|
+
say ""
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module StimulusPdfViewer
|
|
2
|
+
module Rails
|
|
3
|
+
class Engine < ::Rails::Engine
|
|
4
|
+
isolate_namespace StimulusPdfViewer
|
|
5
|
+
|
|
6
|
+
initializer "stimulus-pdf-viewer.assets" do |app|
|
|
7
|
+
# Add our assets to the asset paths
|
|
8
|
+
app.config.assets.paths << root.join("app/assets/javascripts")
|
|
9
|
+
app.config.assets.paths << root.join("app/assets/stylesheets")
|
|
10
|
+
app.config.assets.paths << root.join("app/assets/images")
|
|
11
|
+
|
|
12
|
+
# Precompile the main JS file
|
|
13
|
+
app.config.assets.precompile += %w[
|
|
14
|
+
stimulus-pdf-viewer.esm.js
|
|
15
|
+
stimulus-pdf-viewer.js
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
initializer "stimulus-pdf-viewer.importmap", before: "importmap" do |app|
|
|
20
|
+
if app.config.respond_to?(:importmap)
|
|
21
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "json"
|
|
3
|
+
require "open-uri"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
|
|
6
|
+
namespace :stimulus_pdf_viewer do
|
|
7
|
+
desc "Update vendored assets from stimulus-pdf-viewer npm package"
|
|
8
|
+
task :update, [:version] do |_t, args|
|
|
9
|
+
version = args[:version] || "latest"
|
|
10
|
+
updater = StimulusPdfViewerAssetUpdater.new(version)
|
|
11
|
+
updater.run
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
desc "Check for newer versions of stimulus-pdf-viewer"
|
|
15
|
+
task :check do
|
|
16
|
+
checker = StimulusPdfViewerVersionChecker.new
|
|
17
|
+
checker.run
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class StimulusPdfViewerAssetUpdater
|
|
22
|
+
NPM_REGISTRY_URL = "https://registry.npmjs.org/stimulus-pdf-viewer"
|
|
23
|
+
GEM_ROOT = File.expand_path("../..", __dir__)
|
|
24
|
+
|
|
25
|
+
ASSET_MAPPINGS = {
|
|
26
|
+
"dist/stimulus-pdf-viewer.esm.js" => "app/assets/javascripts/stimulus-pdf-viewer.esm.js",
|
|
27
|
+
"dist/stimulus-pdf-viewer.js" => "app/assets/javascripts/stimulus-pdf-viewer.js",
|
|
28
|
+
"styles/pdf-viewer.scss" => "app/assets/stylesheets/stimulus-pdf-viewer.scss"
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
CURSOR_SOURCE_DIR = "assets/cursors"
|
|
32
|
+
CURSOR_DEST_DIR = "app/assets/images/stimulus-pdf-viewer"
|
|
33
|
+
|
|
34
|
+
def initialize(version)
|
|
35
|
+
@requested_version = version
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def run
|
|
39
|
+
puts "Fetching package info from npm registry..."
|
|
40
|
+
package_info = fetch_package_info
|
|
41
|
+
|
|
42
|
+
@version = resolve_version(package_info)
|
|
43
|
+
puts "Target version: #{@version}"
|
|
44
|
+
|
|
45
|
+
current_version = read_current_version
|
|
46
|
+
if current_version == @version && @requested_version == "latest"
|
|
47
|
+
puts "Already at version #{@version}. Use rake stimulus_pdf_viewer:update[#{@version}] to force reinstall."
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
if current_version == @version
|
|
52
|
+
puts "Reinstalling version #{@version}..."
|
|
53
|
+
else
|
|
54
|
+
puts "Updating from #{current_version} to #{@version}..."
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
Dir.mktmpdir do |tmpdir|
|
|
58
|
+
tarball_path = download_package(package_info, tmpdir)
|
|
59
|
+
extract_package(tarball_path, tmpdir)
|
|
60
|
+
|
|
61
|
+
package_dir = File.join(tmpdir, "package")
|
|
62
|
+
copy_assets(package_dir)
|
|
63
|
+
update_version_file
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
puts "Successfully updated to version #{@version}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def fetch_package_info
|
|
72
|
+
JSON.parse(URI.open(NPM_REGISTRY_URL).read)
|
|
73
|
+
rescue OpenURI::HTTPError => e
|
|
74
|
+
abort "Failed to fetch package info: #{e.message}"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def resolve_version(package_info)
|
|
78
|
+
if @requested_version == "latest"
|
|
79
|
+
package_info["dist-tags"]["latest"]
|
|
80
|
+
else
|
|
81
|
+
unless package_info["versions"].key?(@requested_version)
|
|
82
|
+
available = package_info["versions"].keys.last(10).join(", ")
|
|
83
|
+
abort "Version #{@requested_version} not found. Recent versions: #{available}"
|
|
84
|
+
end
|
|
85
|
+
@requested_version
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def read_current_version
|
|
90
|
+
version_file = File.join(GEM_ROOT, "lib/stimulus_pdf_viewer/rails/version.rb")
|
|
91
|
+
content = File.read(version_file)
|
|
92
|
+
match = content.match(/STIMULUS_PDF_VIEWER_VERSION\s*=\s*["']([^"']+)["']/)
|
|
93
|
+
match ? match[1] : "unknown"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def download_package(package_info, tmpdir)
|
|
97
|
+
tarball_url = package_info["versions"][@version]["dist"]["tarball"]
|
|
98
|
+
tarball_path = File.join(tmpdir, "package.tgz")
|
|
99
|
+
|
|
100
|
+
puts "Downloading #{tarball_url}..."
|
|
101
|
+
File.open(tarball_path, "wb") do |file|
|
|
102
|
+
URI.open(tarball_url) { |remote| file.write(remote.read) }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
tarball_path
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def extract_package(tarball_path, tmpdir)
|
|
109
|
+
puts "Extracting package..."
|
|
110
|
+
system("tar", "-xzf", tarball_path, "-C", tmpdir, exception: true)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def copy_assets(package_dir)
|
|
114
|
+
puts "Copying assets..."
|
|
115
|
+
|
|
116
|
+
ASSET_MAPPINGS.each do |src, dest|
|
|
117
|
+
src_path = File.join(package_dir, src)
|
|
118
|
+
dest_path = File.join(GEM_ROOT, dest)
|
|
119
|
+
|
|
120
|
+
unless File.exist?(src_path)
|
|
121
|
+
warn " Warning: #{src} not found in package"
|
|
122
|
+
next
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
FileUtils.cp(src_path, dest_path)
|
|
126
|
+
puts " #{src} -> #{dest}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
copy_cursors(package_dir)
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def copy_cursors(package_dir)
|
|
133
|
+
cursor_src = File.join(package_dir, CURSOR_SOURCE_DIR)
|
|
134
|
+
cursor_dest = File.join(GEM_ROOT, CURSOR_DEST_DIR)
|
|
135
|
+
|
|
136
|
+
unless Dir.exist?(cursor_src)
|
|
137
|
+
warn " Warning: #{CURSOR_SOURCE_DIR} not found in package"
|
|
138
|
+
return
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
FileUtils.mkdir_p(cursor_dest)
|
|
142
|
+
|
|
143
|
+
Dir.glob(File.join(cursor_src, "*.svg")).each do |svg|
|
|
144
|
+
FileUtils.cp(svg, cursor_dest)
|
|
145
|
+
puts " #{CURSOR_SOURCE_DIR}/#{File.basename(svg)} -> #{CURSOR_DEST_DIR}/"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def update_version_file
|
|
150
|
+
version_file = File.join(GEM_ROOT, "lib/stimulus_pdf_viewer/rails/version.rb")
|
|
151
|
+
content = File.read(version_file)
|
|
152
|
+
|
|
153
|
+
updated = content.gsub(
|
|
154
|
+
/STIMULUS_PDF_VIEWER_VERSION\s*=\s*["'][^"']+["']/,
|
|
155
|
+
"STIMULUS_PDF_VIEWER_VERSION = \"#{@version}\""
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
File.write(version_file, updated)
|
|
159
|
+
puts "Updated STIMULUS_PDF_VIEWER_VERSION to #{@version}"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
class StimulusPdfViewerVersionChecker
|
|
164
|
+
NPM_REGISTRY_URL = "https://registry.npmjs.org/stimulus-pdf-viewer"
|
|
165
|
+
GEM_ROOT = File.expand_path("../..", __dir__)
|
|
166
|
+
|
|
167
|
+
def run
|
|
168
|
+
current = read_current_version
|
|
169
|
+
latest = fetch_latest_version
|
|
170
|
+
|
|
171
|
+
puts "Current vendored version: #{current}"
|
|
172
|
+
puts "Latest npm version: #{latest}"
|
|
173
|
+
|
|
174
|
+
if current == latest
|
|
175
|
+
puts "You are up to date!"
|
|
176
|
+
else
|
|
177
|
+
puts "Update available! Run: rake stimulus_pdf_viewer:update"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def read_current_version
|
|
184
|
+
version_file = File.join(GEM_ROOT, "lib/stimulus_pdf_viewer/rails/version.rb")
|
|
185
|
+
content = File.read(version_file)
|
|
186
|
+
match = content.match(/STIMULUS_PDF_VIEWER_VERSION\s*=\s*["']([^"']+)["']/)
|
|
187
|
+
match ? match[1] : "unknown"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def fetch_latest_version
|
|
191
|
+
response = JSON.parse(URI.open(NPM_REGISTRY_URL).read)
|
|
192
|
+
response["dist-tags"]["latest"]
|
|
193
|
+
rescue OpenURI::HTTPError => e
|
|
194
|
+
abort "Failed to fetch package info: #{e.message}"
|
|
195
|
+
end
|
|
196
|
+
end
|