presently 0.2.0 → 0.3.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/presently/slide.rb +49 -29
- data/lib/presently/slide_renderer.rb +5 -0
- data/lib/presently/version.rb +1 -1
- data/public/_static/index.css +91 -2
- data/public/application.js +30 -3
- data/public/slide.js +52 -0
- data/readme.md +17 -3
- data/releases.md +12 -0
- data/templates/diagram.xrb +5 -0
- data.tar.gz.sig +0 -0
- metadata +34 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: cbb2d491928f3efc616d50e71f949a51d9ac4eca645a981372434a28855f86aa
|
|
4
|
+
data.tar.gz: 1a8eed679adbe70ab3303912f67789fb17cae0a723e6a363a96609ff70ec8e39
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 40fba8bd5e994c33136e8627459a634b0c710539a19e06542ffcdacb525b003cff161418ab860c19a59ba91e8eabd2267a0b2bc004b2d509fc838ba8964ab8ab
|
|
7
|
+
data.tar.gz: e312ec32e02e1d3f21a36eb064ccedc82280984289382ffe996e3c3a03c9973a84da795e6dfd85bbc2b34fd7e4c8834387480128440ffbd5ba8c2773a3625e51
|
checksums.yaml.gz.sig
ADDED
|
Binary file
|
data/lib/presently/slide.rb
CHANGED
|
@@ -21,46 +21,61 @@ module Presently
|
|
|
21
21
|
module Parser
|
|
22
22
|
# Markly extensions enabled for all slide Markdown rendering.
|
|
23
23
|
EXTENSIONS = [:table, :tasklist, :strikethrough, :autolink]
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
module_function
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
# Parse the file and return a {Slide}.
|
|
28
28
|
# @parameter path [String] The file path to parse.
|
|
29
29
|
# @returns [Slide]
|
|
30
30
|
def load(path)
|
|
31
31
|
raw = File.read(path)
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
# Parse once, with native front matter support.
|
|
34
34
|
document = Markly.parse(raw, flags: Markly::UNSAFE | Markly::FRONT_MATTER, extensions: EXTENSIONS)
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
# Extract front matter from the first AST node if present.
|
|
37
37
|
front_matter = nil
|
|
38
38
|
if (front_matter_node = document.first_child) && front_matter_node.type == :front_matter
|
|
39
39
|
front_matter = YAML.safe_load(front_matter_node.string_content)
|
|
40
40
|
front_matter_node.delete
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
# Find the last hrule, which acts as the separator between slide content and presenter notes.
|
|
44
44
|
last_hrule = nil
|
|
45
45
|
document.each{|node| last_hrule = node if node.type == :hrule}
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
if last_hrule
|
|
48
48
|
notes_fragment = Markly::Node.new(:document)
|
|
49
49
|
while child = last_hrule.next
|
|
50
50
|
notes_fragment.append_child(child)
|
|
51
51
|
end
|
|
52
52
|
last_hrule.delete
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
# Extract the last javascript code block from the notes as the slide script.
|
|
55
|
+
script_node = nil
|
|
56
|
+
notes_fragment.each do |node|
|
|
57
|
+
if node.type == :code_block && node.fence_info.to_s.strip == "javascript"
|
|
58
|
+
script_node = node
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
script = nil
|
|
63
|
+
if script_node
|
|
64
|
+
script = script_node.string_content
|
|
65
|
+
script_node.delete
|
|
66
|
+
end
|
|
67
|
+
|
|
54
68
|
content = parse_sections(document.each)
|
|
55
69
|
notes = render_nodes(notes_fragment.each)
|
|
56
70
|
else
|
|
57
71
|
content = parse_sections(document.each)
|
|
58
72
|
notes = nil
|
|
73
|
+
script = nil
|
|
59
74
|
end
|
|
60
|
-
|
|
61
|
-
Slide.new(path, front_matter: front_matter, content: content, notes: notes)
|
|
75
|
+
|
|
76
|
+
Slide.new(path, front_matter: front_matter, content: content, notes: notes, script: script)
|
|
62
77
|
end
|
|
63
|
-
|
|
78
|
+
|
|
64
79
|
# Parse a list of AST nodes into sections based on top-level Markdown headings.
|
|
65
80
|
# Each heading becomes a named key; content before the first heading
|
|
66
81
|
# is collected under `"body"`. Headings inside code blocks are invisible
|
|
@@ -71,7 +86,7 @@ module Presently
|
|
|
71
86
|
sections = {}
|
|
72
87
|
current_key = "body"
|
|
73
88
|
current_nodes = []
|
|
74
|
-
|
|
89
|
+
|
|
75
90
|
nodes.each do |node|
|
|
76
91
|
if node.type == :header
|
|
77
92
|
sections[current_key] = render_nodes(current_nodes) unless current_nodes.empty?
|
|
@@ -81,12 +96,12 @@ module Presently
|
|
|
81
96
|
current_nodes << node
|
|
82
97
|
end
|
|
83
98
|
end
|
|
84
|
-
|
|
99
|
+
|
|
85
100
|
sections[current_key] = render_nodes(current_nodes) unless current_nodes.empty?
|
|
86
|
-
|
|
101
|
+
|
|
87
102
|
sections
|
|
88
103
|
end
|
|
89
|
-
|
|
104
|
+
|
|
90
105
|
# Render a list of AST nodes to HTML via a temporary document.
|
|
91
106
|
# @parameter nodes [Array(Markly::Node)] The nodes to render.
|
|
92
107
|
# @returns [String] The rendered HTML.
|
|
@@ -96,74 +111,79 @@ module Presently
|
|
|
96
111
|
Renderer.new(flags: Markly::UNSAFE, extensions: EXTENSIONS).render(doc)
|
|
97
112
|
end
|
|
98
113
|
end
|
|
99
|
-
|
|
114
|
+
|
|
100
115
|
# Load and parse a slide from a Markdown file.
|
|
101
116
|
# @parameter path [String] The file path to the Markdown slide.
|
|
102
117
|
# @returns [Slide]
|
|
103
118
|
def self.load(path)
|
|
104
119
|
Parser.load(path)
|
|
105
120
|
end
|
|
106
|
-
|
|
121
|
+
|
|
107
122
|
# Initialize a slide with pre-parsed data.
|
|
108
123
|
# @parameter path [String] The file path of the slide.
|
|
109
124
|
# @parameter front_matter [Hash | Nil] The parsed YAML front_matter.
|
|
110
125
|
# @parameter content [Hash(String, String)] Content sections keyed by heading name.
|
|
111
126
|
# @parameter notes [String | Nil] The rendered HTML presenter notes.
|
|
112
|
-
|
|
127
|
+
# @parameter script [String | Nil] JavaScript to execute after the slide renders.
|
|
128
|
+
def initialize(path, front_matter: nil, content: {}, notes: nil, script: nil)
|
|
113
129
|
@path = path
|
|
114
130
|
@front_matter = front_matter
|
|
115
131
|
@content = content
|
|
116
132
|
@notes = notes
|
|
133
|
+
@script = script
|
|
117
134
|
end
|
|
118
|
-
|
|
135
|
+
|
|
119
136
|
# @attribute [String] The file path of the slide.
|
|
120
137
|
attr :path
|
|
121
|
-
|
|
138
|
+
|
|
122
139
|
# @attribute [Hash | Nil] The parsed YAML front_matter.
|
|
123
140
|
attr :front_matter
|
|
124
|
-
|
|
141
|
+
|
|
125
142
|
# @attribute [Hash(String, String)] The content sections keyed by heading name.
|
|
126
143
|
attr :content
|
|
127
|
-
|
|
144
|
+
|
|
128
145
|
# @attribute [String | Nil] The rendered HTML presenter notes.
|
|
129
146
|
attr :notes
|
|
130
|
-
|
|
147
|
+
|
|
148
|
+
# @attribute [String | Nil] JavaScript to execute after the slide renders on the display.
|
|
149
|
+
attr :script
|
|
150
|
+
|
|
131
151
|
# The template to use for rendering this slide.
|
|
132
152
|
# @returns [String] The template name from front_matter, or `"default"`.
|
|
133
153
|
def template
|
|
134
154
|
@front_matter&.fetch("template", "default") || "default"
|
|
135
155
|
end
|
|
136
|
-
|
|
156
|
+
|
|
137
157
|
# The expected duration of this slide in seconds.
|
|
138
158
|
# @returns [Integer] The duration from front_matter, or `60`.
|
|
139
159
|
def duration
|
|
140
160
|
@front_matter&.fetch("duration", 60) || 60
|
|
141
161
|
end
|
|
142
|
-
|
|
162
|
+
|
|
143
163
|
# The title of this slide.
|
|
144
164
|
# @returns [String] The title from front_matter, or the filename without extension.
|
|
145
165
|
def title
|
|
146
166
|
@front_matter&.fetch("title", File.basename(@path, ".md")) || File.basename(@path, ".md")
|
|
147
167
|
end
|
|
148
|
-
|
|
168
|
+
|
|
149
169
|
# Whether this slide should be skipped in the presentation.
|
|
150
170
|
# @returns [Boolean]
|
|
151
171
|
def skip?
|
|
152
172
|
@front_matter&.fetch("skip", false) || false
|
|
153
173
|
end
|
|
154
|
-
|
|
174
|
+
|
|
155
175
|
# The navigation marker for this slide, used in the presenter's jump-to dropdown.
|
|
156
176
|
# @returns [String | Nil] The marker label, or `nil` if not marked.
|
|
157
177
|
def marker
|
|
158
178
|
@front_matter&.fetch("marker", nil)
|
|
159
179
|
end
|
|
160
|
-
|
|
180
|
+
|
|
161
181
|
# The transition type for animating into this slide.
|
|
162
|
-
# @returns [String | Nil] The transition name (e.g. `"fade"`, `"slide-left"`, `"
|
|
182
|
+
# @returns [String | Nil] The transition name (e.g. `"fade"`, `"slide-left"`, `"morph"`), or `nil` for instant swap.
|
|
163
183
|
def transition
|
|
164
184
|
@front_matter&.fetch("transition", nil)
|
|
165
185
|
end
|
|
166
|
-
|
|
186
|
+
|
|
167
187
|
# The line range to focus on for code slides.
|
|
168
188
|
# @returns [Array(Integer, Integer) | Nil] The `[start, end]` line numbers (1-based), or `nil`.
|
|
169
189
|
def focus
|
|
@@ -36,6 +36,11 @@ module Presently
|
|
|
36
36
|
classes = [@css_class, extra_class].compact.join(" ")
|
|
37
37
|
builder.tag(:div, class: classes, data: {template: slide.template}) do
|
|
38
38
|
builder.raw(html)
|
|
39
|
+
if slide.script
|
|
40
|
+
builder.tag(:script, type: "text/slide-script") do
|
|
41
|
+
builder.raw(slide.script)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
end
|
data/lib/presently/version.rb
CHANGED
data/public/_static/index.css
CHANGED
|
@@ -40,6 +40,7 @@ html, body {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
.slide-inner {
|
|
43
|
+
position: relative;
|
|
43
44
|
width: 100%;
|
|
44
45
|
height: 100%;
|
|
45
46
|
display: flex;
|
|
@@ -63,6 +64,14 @@ html, body {
|
|
|
63
64
|
|
|
64
65
|
.default-template .slide-body li {
|
|
65
66
|
margin-bottom: 0.5em;
|
|
67
|
+
transition: opacity 0.4s ease, transform 0.4s ease;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@starting-style {
|
|
71
|
+
.default-template .slide-body li {
|
|
72
|
+
opacity: 0;
|
|
73
|
+
transform: translateX(-0.5em);
|
|
74
|
+
}
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
/* Title template */
|
|
@@ -125,6 +134,16 @@ html, body {
|
|
|
125
134
|
margin-bottom: 0.4em;
|
|
126
135
|
}
|
|
127
136
|
|
|
137
|
+
/* Diagram template */
|
|
138
|
+
.diagram-template {
|
|
139
|
+
padding: 0;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.diagram-template .slide-body > div {
|
|
143
|
+
position: absolute;
|
|
144
|
+
box-sizing: border-box;
|
|
145
|
+
}
|
|
146
|
+
|
|
128
147
|
/* Image template */
|
|
129
148
|
.image-template .slide-caption {
|
|
130
149
|
font-size: 1.6rem;
|
|
@@ -353,12 +372,12 @@ html[data-transition="slide-right"]::view-transition-new(slide-container) {
|
|
|
353
372
|
/* Magic move — the browser interpolates position/size for matched
|
|
354
373
|
view-transition-name elements. No cross-fade on the container
|
|
355
374
|
to avoid background dimming. */
|
|
356
|
-
html[data-transition="
|
|
375
|
+
html[data-transition="morph"]::view-transition-old(slide-container) {
|
|
357
376
|
animation: none;
|
|
358
377
|
opacity: 0;
|
|
359
378
|
}
|
|
360
379
|
|
|
361
|
-
html[data-transition="
|
|
380
|
+
html[data-transition="morph"]::view-transition-new(slide-container) {
|
|
362
381
|
animation: none;
|
|
363
382
|
}
|
|
364
383
|
|
|
@@ -392,6 +411,72 @@ html[data-transition="magic-move"]::view-transition-new(slide-container) {
|
|
|
392
411
|
to { transform: translateX(0); opacity: 1; }
|
|
393
412
|
}
|
|
394
413
|
|
|
414
|
+
/* ========================
|
|
415
|
+
BUILD EFFECTS
|
|
416
|
+
======================== */
|
|
417
|
+
|
|
418
|
+
/* Suppress both pseudo-elements for hidden build elements so they
|
|
419
|
+
neither crossfade in nor crossfade out during the transition. */
|
|
420
|
+
::view-transition-old(.build-hidden),
|
|
421
|
+
::view-transition-new(.build-hidden) {
|
|
422
|
+
display: none;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
/* Fade */
|
|
426
|
+
::view-transition-new(.build-fade) {
|
|
427
|
+
animation: vt-fade-in 0.4s ease;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/* Fly in from left */
|
|
431
|
+
@keyframes build-fly-in-left {
|
|
432
|
+
from { transform: translateX(-2rem); opacity: 0; }
|
|
433
|
+
to { transform: translateX(0); opacity: 1; }
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
::view-transition-new(.build-fly-left) {
|
|
437
|
+
animation: build-fly-in-left 0.4s ease;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/* Fly in from right */
|
|
441
|
+
@keyframes build-fly-in-right {
|
|
442
|
+
from { transform: translateX(2rem); opacity: 0; }
|
|
443
|
+
to { transform: translateX(0); opacity: 1; }
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
::view-transition-new(.build-fly-right) {
|
|
447
|
+
animation: build-fly-in-right 0.4s ease;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* Fly in from bottom */
|
|
451
|
+
@keyframes build-fly-in-up {
|
|
452
|
+
from { transform: translateY(2rem); opacity: 0; }
|
|
453
|
+
to { transform: translateY(0); opacity: 1; }
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
::view-transition-new(.build-fly-up) {
|
|
457
|
+
animation: build-fly-in-up 0.4s ease;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/* Fly in from top */
|
|
461
|
+
@keyframes build-fly-in-down {
|
|
462
|
+
from { transform: translateY(-2rem); opacity: 0; }
|
|
463
|
+
to { transform: translateY(0); opacity: 1; }
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
::view-transition-new(.build-fly-down) {
|
|
467
|
+
animation: build-fly-in-down 0.4s ease;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/* Scale in */
|
|
471
|
+
@keyframes build-scale-in {
|
|
472
|
+
from { transform: scale(0.8); opacity: 0; }
|
|
473
|
+
to { transform: scale(1); opacity: 1; }
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
::view-transition-new(.build-scale) {
|
|
477
|
+
animation: build-scale-in 0.4s ease;
|
|
478
|
+
}
|
|
479
|
+
|
|
395
480
|
/* ========================
|
|
396
481
|
PRESENTER VIEW
|
|
397
482
|
======================== */
|
|
@@ -617,6 +702,10 @@ html[data-transition="magic-move"]::view-transition-new(slide-container) {
|
|
|
617
702
|
margin: 0.3em 0;
|
|
618
703
|
}
|
|
619
704
|
|
|
705
|
+
.notes-content em {
|
|
706
|
+
color: var(--ahead);
|
|
707
|
+
}
|
|
708
|
+
|
|
620
709
|
.notes .no-notes {
|
|
621
710
|
opacity: 0.4;
|
|
622
711
|
font-style: italic;
|
data/public/application.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Live } from 'live';
|
|
2
2
|
import Syntax from '@socketry/syntax';
|
|
3
|
+
import { Slide } from './slide.js';
|
|
3
4
|
|
|
4
5
|
const live = Live.start();
|
|
5
6
|
|
|
@@ -65,6 +66,29 @@ function applyCodeFocus() {
|
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
|
|
69
|
+
// Run the script for a single slide element.
|
|
70
|
+
// Wrapped in try/catch so syntax errors don't crash the presentation.
|
|
71
|
+
function runScript(slideEl) {
|
|
72
|
+
const scriptEl = slideEl.querySelector('script[type="text/slide-script"]');
|
|
73
|
+
if (!scriptEl) return;
|
|
74
|
+
|
|
75
|
+
const container = slideEl.querySelector('.slide-body') ?? slideEl;
|
|
76
|
+
const slide = new Slide(container);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const fn = new Function('slide', scriptEl.textContent);
|
|
80
|
+
fn(slide);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Slide script error:', error);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Run scripts for all slide elements currently in the DOM.
|
|
87
|
+
function runSlideScripts() {
|
|
88
|
+
document.querySelectorAll('.slide').forEach(runScript);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
68
92
|
// Detect the transition type from the incoming HTML before morphdom applies it.
|
|
69
93
|
function detectTransition(html) {
|
|
70
94
|
const match = html.match(/data-transition="([^"]+)"/);
|
|
@@ -82,11 +106,12 @@ live.update = function(id, html, options) {
|
|
|
82
106
|
|
|
83
107
|
if (transition && document.startViewTransition && !activeTransition) {
|
|
84
108
|
document.documentElement.dataset.transition = transition;
|
|
85
|
-
|
|
109
|
+
|
|
86
110
|
activeTransition = document.startViewTransition(() => {
|
|
87
111
|
originalUpdate(id, html, options);
|
|
112
|
+
runSlideScripts();
|
|
88
113
|
});
|
|
89
|
-
|
|
114
|
+
|
|
90
115
|
activeTransition.finished.finally(() => {
|
|
91
116
|
delete document.documentElement.dataset.transition;
|
|
92
117
|
activeTransition = null;
|
|
@@ -95,6 +120,7 @@ live.update = function(id, html, options) {
|
|
|
95
120
|
});
|
|
96
121
|
} else {
|
|
97
122
|
originalUpdate(id, html, options);
|
|
123
|
+
runSlideScripts();
|
|
98
124
|
Syntax.highlight();
|
|
99
125
|
applyCodeFocus();
|
|
100
126
|
}
|
|
@@ -108,8 +134,9 @@ const observer = new MutationObserver(() => {
|
|
|
108
134
|
});
|
|
109
135
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
110
136
|
|
|
111
|
-
// Initial focus application:
|
|
137
|
+
// Initial focus and script application:
|
|
112
138
|
applyCodeFocus();
|
|
139
|
+
runSlideScripts();
|
|
113
140
|
|
|
114
141
|
// Jump-to select: forward the selected slide index to the presenter view.
|
|
115
142
|
document.addEventListener('change', (event) => {
|
data/public/slide.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Represents a collection of elements within a slide to be revealed sequentially.
|
|
2
|
+
// Has no side effects until build() is called.
|
|
3
|
+
export class SlideElements {
|
|
4
|
+
constructor(elements) {
|
|
5
|
+
this._elements = elements;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Show the first n elements and hide the rest.
|
|
9
|
+
// Assigns view-transition-names and applies build visibility in one step.
|
|
10
|
+
// @parameter n [Integer] Number of elements to show.
|
|
11
|
+
// @parameter options [Object]
|
|
12
|
+
// group: prefix for view-transition-name (default: "build")
|
|
13
|
+
// effect: "fade", "fly-up", "fly-down", "fly-left", "fly-right", "scale"
|
|
14
|
+
build(n, options = {}) {
|
|
15
|
+
const prefix = options.group || 'build';
|
|
16
|
+
|
|
17
|
+
this._elements.forEach((element, index) => {
|
|
18
|
+
element.style.viewTransitionName = `${prefix}-${index + 1}`;
|
|
19
|
+
|
|
20
|
+
if (index < n) {
|
|
21
|
+
element.style.visibility = 'visible';
|
|
22
|
+
// Newly revealed element: apply the enter effect.
|
|
23
|
+
// Already-visible elements: clear any class so they morph normally.
|
|
24
|
+
element.style.viewTransitionClass = (index === n - 1 && options.effect)
|
|
25
|
+
? `build-${options.effect}`
|
|
26
|
+
: '';
|
|
27
|
+
} else {
|
|
28
|
+
element.style.visibility = 'hidden';
|
|
29
|
+
// Hidden elements: suppress both pseudo-elements so they don't
|
|
30
|
+
// crossfade in or out during the transition.
|
|
31
|
+
element.style.viewTransitionClass = 'build-hidden';
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// The scripting context passed to each slide's javascript block.
|
|
38
|
+
// Scopes element queries to the slide body.
|
|
39
|
+
export class Slide {
|
|
40
|
+
constructor(container) {
|
|
41
|
+
this._container = container;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Find elements within this slide matching the given CSS selector.
|
|
45
|
+
// Use comma-separated selectors to combine multiple element types, e.g. "h2, li".
|
|
46
|
+
// @parameter selector [String] A CSS selector scoped to the slide body.
|
|
47
|
+
// @returns [SlideElements]
|
|
48
|
+
find(selector) {
|
|
49
|
+
const elements = Array.from(this._container.querySelectorAll(selector));
|
|
50
|
+
return new SlideElements(elements);
|
|
51
|
+
}
|
|
52
|
+
}
|
data/readme.md
CHANGED
|
@@ -17,13 +17,27 @@ A web-based presentation tool built with [Lively](https://github.com/socketry/li
|
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
19
19
|
|
|
20
|
-
Please see the [project documentation](https://github.
|
|
20
|
+
Please see the [project documentation](https://socketry.github.io/presently/) for more details.
|
|
21
21
|
|
|
22
|
-
- [
|
|
22
|
+
- [Animating Slides](https://socketry.github.io/presently/guides/animating-slides/index) - This guide explains how to animate content within slides using the `morph` transition and the slide scripting system.
|
|
23
|
+
|
|
24
|
+
- [Getting Started](https://socketry.github.io/presently/guides/getting-started/index) - This guide explains how to use `presently` to create and deliver web-based presentations using Markdown slides.
|
|
23
25
|
|
|
24
26
|
## Releases
|
|
25
27
|
|
|
26
|
-
Please see the [project releases](https://github.
|
|
28
|
+
Please see the [project releases](https://socketry.github.io/presently/releases/index) for all releases.
|
|
29
|
+
|
|
30
|
+
### v0.3.0
|
|
31
|
+
|
|
32
|
+
- Add `diagram` template with a `position: relative` container — direct `<div>` children are `position: absolute` by default for free-form layouts.
|
|
33
|
+
- All slide templates now have `position: relative` on the slide inner container, allowing absolutely positioned overlays in any template.
|
|
34
|
+
- Add slide scripting: a fenced ` ```javascript ``` ` block at the end of presenter notes is extracted and executed in the browser after each slide renders. The script receives a `slide` object scoped to the slide body.
|
|
35
|
+
- Add `Slide#find(selector)` — a pure CSS selector query returning a `SlideElements` collection with no side effects.
|
|
36
|
+
- Add `SlideElements#build(n, options)` — shows the first `n` matched elements, hides the rest, and assigns `view-transition-name` for morph transition matching. Accepts `group` (name prefix) and `effect` (entry animation) options.
|
|
37
|
+
- Add build effects via `view-transition-class`: `fade`, `fly-left`, `fly-right`, `fly-up`, `fly-down`, `scale`. Requires Chromium 125+; degrades gracefully to instant appear in other browsers.
|
|
38
|
+
- Rename `magic-move` transition to `morph`.
|
|
39
|
+
- Italic text in presenter notes is styled in amber to distinguish stage directions from spoken words.
|
|
40
|
+
- Add transitions guide and animating slides guide to documentation.
|
|
27
41
|
|
|
28
42
|
### v0.2.0
|
|
29
43
|
|
data/releases.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.3.0
|
|
4
|
+
|
|
5
|
+
- Add `diagram` template with a `position: relative` container — direct `<div>` children are `position: absolute` by default for free-form layouts.
|
|
6
|
+
- All slide templates now have `position: relative` on the slide inner container, allowing absolutely positioned overlays in any template.
|
|
7
|
+
- Add slide scripting: a fenced ` ```javascript ``` ` block at the end of presenter notes is extracted and executed in the browser after each slide renders. The script receives a `slide` object scoped to the slide body.
|
|
8
|
+
- Add `Slide#find(selector)` — a pure CSS selector query returning a `SlideElements` collection with no side effects.
|
|
9
|
+
- Add `SlideElements#build(n, options)` — shows the first `n` matched elements, hides the rest, and assigns `view-transition-name` for morph transition matching. Accepts `group` (name prefix) and `effect` (entry animation) options.
|
|
10
|
+
- Add build effects via `view-transition-class`: `fade`, `fly-left`, `fly-right`, `fly-up`, `fly-down`, `scale`. Requires Chromium 125+; degrades gracefully to instant appear in other browsers.
|
|
11
|
+
- Rename `magic-move` transition to `morph`.
|
|
12
|
+
- Italic text in presenter notes is styled in amber to distinguish stage directions from spoken words.
|
|
13
|
+
- Add transitions guide and animating slides guide to documentation.
|
|
14
|
+
|
|
3
15
|
## v0.2.0
|
|
4
16
|
|
|
5
17
|
- Use Markly's native front matter parser (`Markly::FRONT_MATTER`) instead of manual string splitting, parsing each slide document once and extracting front matter directly from the AST.
|
data.tar.gz.sig
ADDED
|
Binary file
|
metadata
CHANGED
|
@@ -1,12 +1,41 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: presently
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
8
8
|
bindir: bin
|
|
9
|
-
cert_chain:
|
|
9
|
+
cert_chain:
|
|
10
|
+
- |
|
|
11
|
+
-----BEGIN CERTIFICATE-----
|
|
12
|
+
MIIE2DCCA0CgAwIBAgIBATANBgkqhkiG9w0BAQsFADBhMRgwFgYDVQQDDA9zYW11
|
|
13
|
+
ZWwud2lsbGlhbXMxHTAbBgoJkiaJk/IsZAEZFg1vcmlvbnRyYW5zZmVyMRIwEAYK
|
|
14
|
+
CZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJuejAeFw0yMjA4MDYwNDUz
|
|
15
|
+
MjRaFw0zMjA4MDMwNDUzMjRaMGExGDAWBgNVBAMMD3NhbXVlbC53aWxsaWFtczEd
|
|
16
|
+
MBsGCgmSJomT8ixkARkWDW9yaW9udHJhbnNmZXIxEjAQBgoJkiaJk/IsZAEZFgJj
|
|
17
|
+
bzESMBAGCgmSJomT8ixkARkWAm56MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIB
|
|
18
|
+
igKCAYEAomvSopQXQ24+9DBB6I6jxRI2auu3VVb4nOjmmHq7XWM4u3HL+pni63X2
|
|
19
|
+
9qZdoq9xt7H+RPbwL28LDpDNflYQXoOhoVhQ37Pjn9YDjl8/4/9xa9+NUpl9XDIW
|
|
20
|
+
sGkaOY0eqsQm1pEWkHJr3zn/fxoKPZPfaJOglovdxf7dgsHz67Xgd/ka+Wo1YqoE
|
|
21
|
+
e5AUKRwUuvaUaumAKgPH+4E4oiLXI4T1Ff5Q7xxv6yXvHuYtlMHhYfgNn8iiW8WN
|
|
22
|
+
XibYXPNP7NtieSQqwR/xM6IRSoyXKuS+ZNGDPUUGk8RoiV/xvVN4LrVm9upSc0ss
|
|
23
|
+
RZ6qwOQmXCo/lLcDUxJAgG95cPw//sI00tZan75VgsGzSWAOdjQpFM0l4dxvKwHn
|
|
24
|
+
tUeT3ZsAgt0JnGqNm2Bkz81kG4A2hSyFZTFA8vZGhp+hz+8Q573tAR89y9YJBdYM
|
|
25
|
+
zp0FM4zwMNEUwgfRzv1tEVVUEXmoFCyhzonUUw4nE4CFu/sE3ffhjKcXcY//qiSW
|
|
26
|
+
xm4erY3XAgMBAAGjgZowgZcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
|
27
|
+
BBYEFO9t7XWuFf2SKLmuijgqR4sGDlRsMC4GA1UdEQQnMCWBI3NhbXVlbC53aWxs
|
|
28
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MC4GA1UdEgQnMCWBI3NhbXVlbC53aWxs
|
|
29
|
+
aWFtc0BvcmlvbnRyYW5zZmVyLmNvLm56MA0GCSqGSIb3DQEBCwUAA4IBgQB5sxkE
|
|
30
|
+
cBsSYwK6fYpM+hA5B5yZY2+L0Z+27jF1pWGgbhPH8/FjjBLVn+VFok3CDpRqwXCl
|
|
31
|
+
xCO40JEkKdznNy2avOMra6PFiQyOE74kCtv7P+Fdc+FhgqI5lMon6tt9rNeXmnW/
|
|
32
|
+
c1NaMRdxy999hmRGzUSFjozcCwxpy/LwabxtdXwXgSay4mQ32EDjqR1TixS1+smp
|
|
33
|
+
8C/NCWgpIfzpHGJsjvmH2wAfKtTTqB9CVKLCWEnCHyCaRVuKkrKjqhYCdmMBqCws
|
|
34
|
+
JkxfQWC+jBVeG9ZtPhQgZpfhvh+6hMhraUYRQ6XGyvBqEUe+yo6DKIT3MtGE2+CP
|
|
35
|
+
eX9i9ZWBydWb8/rvmwmX2kkcBbX0hZS1rcR593hGc61JR6lvkGYQ2MYskBveyaxt
|
|
36
|
+
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
|
37
|
+
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
|
38
|
+
-----END CERTIFICATE-----
|
|
10
39
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
40
|
dependencies:
|
|
12
41
|
- !ruby/object:Gem::Dependency
|
|
@@ -1029,10 +1058,12 @@ files:
|
|
|
1029
1058
|
- public/_static/index.css
|
|
1030
1059
|
- public/application.js
|
|
1031
1060
|
- public/mermaid-diagram.js
|
|
1061
|
+
- public/slide.js
|
|
1032
1062
|
- readme.md
|
|
1033
1063
|
- releases.md
|
|
1034
1064
|
- templates/code.xrb
|
|
1035
1065
|
- templates/default.xrb
|
|
1066
|
+
- templates/diagram.xrb
|
|
1036
1067
|
- templates/fill.xrb
|
|
1037
1068
|
- templates/image.xrb
|
|
1038
1069
|
- templates/section.xrb
|
|
@@ -1043,6 +1074,7 @@ homepage: https://github.com/socketry/presently
|
|
|
1043
1074
|
licenses:
|
|
1044
1075
|
- MIT
|
|
1045
1076
|
metadata:
|
|
1077
|
+
documentation_uri: https://socketry.github.io/presently/
|
|
1046
1078
|
source_code_uri: https://github.com/socketry/presently.git
|
|
1047
1079
|
rdoc_options: []
|
|
1048
1080
|
require_paths:
|
metadata.gz.sig
ADDED
|
Binary file
|