wani 0.1.14 → 0.1.15

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f47f0147e24156fd966b89b0185cacd07d7afd45d285c0a0d607d44de3cb6406
4
- data.tar.gz: d8209c67e888d8e909551c0842167724fa8562934fc29f76a874de0a11c2d96d
3
+ metadata.gz: 7082411823ce9ed8947c371bc7e16d786fc39f6e90a2b6d3f7a9d184b256fdcb
4
+ data.tar.gz: 3206f7bc05c39168c14007f5c958e07a4e04ddc4791ca05f7fc65e4dbd7969fc
5
5
  SHA512:
6
- metadata.gz: 0a89984cbbbb6c8466b69b89affab16e36d0791bf63bc7ecc687ee06765be2899f6f3af75b526463e26ba5cf52917b6b1f09d5584af708456365cb2eb80e71f1
7
- data.tar.gz: 565853474a5a6054b46042b8bced8759a4339d12845fd4ccc2fe28f6e862013c9c802913543cf68707e166d7d80f1fdadcce7df6d9e3cdfa087f53a1640115c7
6
+ metadata.gz: cc53a8dbcc69c9f768f2af129f650d71263f8239ce0416b6c48420681f958a60feb5012402eea9f11478fa04a32a660c4f3fcec3c48111eb6ae19d9e81be5f70
7
+ data.tar.gz: dd12aa99c08476117ed9e76c5dd2bab050680ba2e6d79d3ed8998c29c625e3353851d4d79206ed2f89906bf99eb4bff95c362e4f482a86a9869bcb7cf44ba9f1
data/README.md CHANGED
@@ -2,33 +2,23 @@
2
2
 
3
3
  Markdown からスライドを作り、ブラウザー内の Ruby でプレゼンテーションするツールです。
4
4
  Rabbit へのオマージュとして、因幡の白兎に登場するワニ(鮫)にちなんだ名前です。
5
+ 兎とワニがスライドの進み具合を楽しく伝えます。
5
6
 
6
7
  **Markdownを書いたら、あとは話す準備を。**
7
8
 
8
- waniは、内容に合わせて文字サイズを自動調整するプレゼンテーションツールです。
9
- タイトルも本文もスライドに収まる大きさに自動調整。
10
- 文字サイズの調整に手をかけず、伝えたい内容に集中してください。
9
+ waniは文字サイズを自動調整するプレゼンテーションツールです。
10
+ 表紙のタイトルは中央に、本文は固定サイズで右下に右揃えで表示します。タイトルと本文が重なる場合は警告します。2枚目以降はタイトルも本文もスライドに収まる限り最も大きく表示します。
11
+ 文字サイズの調整に手間を掛けません。
11
12
 
12
- 兎とワニがスライドの進み具合を楽しく伝えます。
13
13
  持ち時間も、発表の演出に。
14
14
  最後の5秒は観客と一緒にカウントダウン。
15
15
  時間になるとワニが登場し、発表のフィナーレを飾ります。
16
16
 
17
- <p align="center">
18
- <img src="docs/images/wani-characters.png" alt="ノートパソコンを使う兎と、それを見守るワニ" width="640">
19
- </p>
20
-
21
- <p align="center">
22
- <img src="docs/images/rabbit.png" alt="ノートパソコンを持った兎" width="240">
23
- <img src="docs/images/wani.png" alt="笑顔のワニ" width="240">
24
- </p>
25
-
26
17
  ## インストールと開始
27
18
 
28
19
  ### インストール
29
20
 
30
21
  [RubyGems.org](https://rubygems.org/gems/wani) からインストールできます。
31
- Ruby 3.2 以上が必要です。
32
22
 
33
23
  ```sh
34
24
  gem install wani
@@ -47,11 +37,10 @@ wani check
47
37
  wani serve
48
38
  ```
49
39
 
40
+ http://127.0.0.1:8000/ をブラウザーで開きます。
50
41
 
51
- 表示された http://127.0.0.1:8000/ をブラウザーで開きます。
52
-
53
- `new`コマンドで生成されるプロジェクトには、Markdown記法を紹介する10ページの `slides.md` とサンプルSVG画像が含まれます。
54
- `slides.md` を編集したらブラウザーを再読み込みしてください。
42
+ `new`コマンドで生成されるプロジェクトには、Markdown記法を紹介する10ページの `slides.md` とサンプルSVG画像が含まれています。
43
+ `slides.md` の変更はブラウザーを再読み込みすると反映されます。
55
44
 
56
45
  ## Markdown記法
57
46
 
@@ -79,16 +68,21 @@ notes:
79
68
  見出しレベルが違っても、タイトルのデザインは同じです。
80
69
  ```
81
70
 
82
- - 独立した `---` の行でスライドを分割
83
- - 各ページの最初の見出しをタイトルに
84
- - 本文には段落、強調、リスト、引用、コード、リンク、画像、表が使用可能
85
- - コードフェンスに `ruby` や `js` などの言語名を付けると、スライド・ノート・PDFで構文ハイライトを適用
86
- - 画像は、slides.mdを基準に相対パスで記述
87
- - `<!-- notes: ... -->` は発表者ノート
88
- - `<!-- page-footnote: ... -->` はページごとの脚注
71
+ - タイトル:各ページの最初の見出し
72
+ - 画像:slides.mdを基準に相対パスで記述
73
+ - 発表者ノーと:`<!-- notes: ... -->`
74
+ - 本文: 段落、強調、リスト、引用、コード、リンク、画像、表が使用可能
75
+ - ページ区切り : `---`
89
76
 
90
77
  詳細は [Markdown記法ガイド](docs/markdown.md) を参照してください。
91
78
 
79
+ ## フォント文字サイズを自動調整
80
+
81
+ - タイトルは1行に収める
82
+ - 本文は全角20文字当の幅を超える場合は自然に折り返し
83
+ - 明示的な改行は維持
84
+ - 文字サイズは本文全体で共通
85
+
92
86
  ## コマンド紹介
93
87
 
94
88
  ```sh
@@ -162,62 +156,44 @@ wani --help
162
156
 
163
157
  ## 発表タイマー
164
158
 
165
- hashなしのURLで開くと、最初のスライドに半透明の黒いベールと「スタート」ボタンを表示します。
166
- 待機中はページ移動と計測を止め、ボタンを押すとベールが消えてタイマーが始まります。Fキーで全画面にして待機できます。
167
- `#slide=1` などのhash付きURLでは、読み込み完了から計測します。
168
- 既定では5分で時間切れ画面を表示します。
169
- Front Matter の `time_limit` に分数を指定して変更できます(小数も可)。
170
- `time_limit: 0` でタイマーを無効にします。
159
+ 最初のスライドに半透明の黒いベールと「スタート」ボタンを表示します。
160
+ URL でスライド番号を指定した場合は、ボタンを表示せず、タイマーも使用しません。
161
+ ボタンを押すとベールが消えてタイマーが始まります。
171
162
 
172
163
  残り30秒になると、全画面表示中もカウントダウンを表示します。
173
- 残り5秒は中央に数字を表示し、画面の短辺に対して20%・40%・60%・80%・100%と強調サイズを増やします。
174
- 各数字は0.8秒で通常サイズに戻ります。動きを減らす設定では拡大アニメーションを省略します。
164
+ 残り5秒は中央に数字を表示します。
175
165
  時間切れになると、兎柄フードをくわえたワニ(さめ)の画像を表示します。
176
- 画像は軽量なWebP形式で配布し、発表開始前に読み込みとデコードを済ませて、時間切れの文字と同時に表示します。
177
166
  左右キーまたは「スライドに戻る」ボタンで、時間切れ直前のスライドに戻れます。
178
- 戻った後は通常通りページを移動でき、時間切れ画面は再表示しません。
179
- 最終スライドの表示中はタイマーを一時停止し、カウントダウンも時間切れ画面も表示しません。
180
- 前のスライドに戻ると、停止前の残り時間から再開します。1ページだけの場合も停止します。
181
- ページを再読み込みするとタイマーも最初から始まります。
182
- 開始待ち画面、タイマー、時間切れ画面はPDFには含めません。
183
167
 
184
- ## スライド
168
+ 既定では5分です。
169
+ Front Matter の `time_limit` に分数を指定して変更できます(小数も可)。
170
+ `time_limit: 0` でタイマーを無効にします。
171
+
172
+
173
+ ## スライドのフォーマット
185
174
 
186
175
  - 1920×1080、16:9
187
176
  - 白背景
188
177
  - 画像は比率を維持して縦に配置し、画像だけの場合は本文領域を広く使います。
189
- - `#slide=3` のようなハッシュ付き URL で特定のスライドを開く
190
-
191
- ### 画像とフォントを読み込み、幅と高さに合わせて文字サイズを自動調整
192
- - タイトルは1行に収める
193
- - 本文の段落・見出し・箇条書きは、使用フォントの全角20文字(「あ」20個)相当の幅以下なら折り返さず、それを超える場合は自然に折り返し
194
- - 明示的な改行は維持。改行ごとに幅を判定
195
- - 文字サイズは本文全体で共通
196
178
 
197
-
198
- ### 警告
179
+ ## 警告
199
180
 
200
181
  - 収まらない内容に警告を表示
201
182
  - 画像の読み込みを最大15秒待ち、失敗やタイムアウトを通知
202
183
 
203
184
  ## 印刷
204
185
 
205
- ツールバーの「PDF」「ノート付き PDF」から印刷ダイアログを開き、PDFとして保存します。
186
+ ツールバーの「PDF」「ノート付き PDF」から印刷ダイアログを開き、PDFとして保存できます。
206
187
  Ctrl+P でも印刷できます。
188
+ 用紙は320×180mmを指定し、ブラウザーのヘッダー・フッターはオフにしてください。
207
189
 
208
- - 用紙は320×180mmを指定し、ブラウザーのヘッダー・フッターはオフにしてください。
209
- - 通常版は1スライド1ページ、ノート付きは各スライドの次に対応するノートを置きます。
210
- - 画像・フォントの完了後に印刷し、ページごとの脚注とページ番号を出力します。兎と鰐の装飾は常に非表示です。
211
-
190
+ - 通常版は1スライド1ページ、ノート付きは各スライドの次に対応するノートを配置
191
+ - ページごとの脚注とページ番号を出力
212
192
 
213
193
  ## 同梱フォント
214
194
 
215
195
  日本語・英数字の標準書体は **IBM Plex Sans JP** です。
216
- 通常と太字のフォントを gem に同梱します。
217
-
218
- 本文・タイトル・発表者画面・PDF に同じ書体を使います。
219
- コードには **UDEV Gothic** を使い、日本語も半角1:全角2の等幅で表示します。
220
- UDEV Gothic も通常・太字を同梱するため、外部への通信やOSへのインストールは不要です。
196
+ コードは **UDEV Gothic** を使い、日本語も半角1:全角2の等幅で表示します。
221
197
 
222
198
  フォントは SIL Open Font License 1.1 です。原文のライセンス・著作権表示と配布元・チェックサムは
223
199
  [third_party/ibm-plex-sans-jp](third_party/ibm-plex-sans-jp/README.md) と
@@ -226,7 +202,7 @@ UDEV Gothic も通常・太字を同梱するため、外部への通信やOSへ
226
202
  ## フォントを変更する
227
203
 
228
204
  タイトルと本文には、[Fontsource](https://fontsource.org/fonts)のフォントを追加して使えます。
229
- フォントの追加時にはインターネット接続が必要です。追加後はオフラインで表示・PDF出力ができ、OSへのフォントのインストールやNode.js・npmは不要です。
205
+ フォントの追加時にはインターネット接続が必要です。追加後はオフラインでも表示・PDF出力ができます。
230
206
 
231
207
  ### Fontsourceのフォントを使う
232
208
 
@@ -305,24 +281,6 @@ wani font remove noto-sans-jp
305
281
 
306
282
  スライドで使用中の場合は確認を求めます。削除する場合は、Front Matterの`font`も削除するか、別の追加済みフォントに変更してください。
307
283
 
308
- ### 他の人に渡す場合
309
-
310
- 追加したフォントは`fonts/ID/`、登録情報は`.wani/fonts.yml`に保存されます。
311
- `slides.md`や画像と一緒に、`fonts/`と`.wani/fonts.yml`も保存・配布してください。`.wani`は隠しディレクトリなので、コピーし忘れないようにしてください。
312
-
313
- 各フォントのライセンスは`fonts/ID/LICENSE`に保存されます。Web公開・再配布・PDF配布の際は、そのフォントのライセンス条件を確認してください。
314
-
315
- ## 動作環境と制約
316
-
317
- 確認済みの環境は Linux / Ruby 4.0.7 / Chrome 156.0.8078.47 です。
318
- Windows・WSL2、Edge / Firefox / Safari、通常ブラウザーの印刷ダイアログ操作は未検証です。
319
-
320
- WSL2 内で `wani serve` を起動した場合は、Windows 側のブラウザーで http://localhost:8000/ を開きます。
321
- 環境のポート転送設定に応じて `--bind 0.0.0.0` を指定してください。
322
-
323
- 複雑に入れ子になった Markdown 方言、最適な画像の自動配置、ライブリロードは対象外です。
324
- 発表者画面はメイン画面の終了・再読み込み後に開き直してください。
325
-
326
284
  ## 開発者向けドキュメント
327
285
 
328
286
  - [アーキテクチャ](docs/architecture.md):共通モジュール、静的配信、wasm・ローダー、フォント、レイアウトの設計
data/assets/build.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "0.1.14",
3
- "source_sha256": "c3e705b68ea3335c7704a5be6989fd09d40d0bbc6f8dd6a2d26dd7ea9a49d05a",
2
+ "version": "0.1.15",
3
+ "source_sha256": "a9ec44f0f122168b5bdce3783ea9188f6955f1835f53ad67258bebcff1252aaf",
4
4
  "loader_commit": "dc6b0ffd5800ed9bfc49268ed8021d456f10ddf8",
5
- "wasm_sha256": "feac70ebd959b1aa5d8f628fce3bb8d5570b4404a3128cd37f721f611fe62d83"
5
+ "wasm_sha256": "bad1062bd0f007e51f7278365f14537dd974666f8c6442c304c13223cb78f3e4"
6
6
  }
data/assets/style.css CHANGED
@@ -53,6 +53,10 @@ button:focus-visible, a:focus-visible { outline: 2px solid #557e64; outline-offs
53
53
  .titleless .body > * { flex-shrink: 0; }
54
54
  .titleless .body > :last-child { margin-bottom: 0; }
55
55
  .titleless .body img { margin-inline: auto; }
56
+ .cover .title { margin-top: 396px; text-align: center; }
57
+ .cover .body { display: flex; flex-direction: column; align-items: flex-end; justify-content: flex-end; text-align: right; }
58
+ .cover .body > * { flex-shrink: 0; max-width: 100%; }
59
+ .cover .body > :last-child { margin-bottom: 0; }
56
60
  .body ul, .body ol { margin: .2em 0 .4em; padding-left: 1.35em; }
57
61
  .body pre { margin: .3em 0; padding: .4em .55em; background: #f3f6f2; white-space: pre-wrap; tab-size: 2; }
58
62
  .titleless .body pre { width: fit-content; max-width: 100%; margin-inline: auto; text-align: left; }
@@ -75,11 +79,15 @@ button:focus-visible, a:focus-visible { outline: 2px solid #557e64; outline-offs
75
79
  .rabbit-image { display: block; width: 84px; height: 40px; }
76
80
  .decorations[data-direction="left"] .rabbit-image { scale: -1 1; }
77
81
  .slide.active .bridge-segment[data-appearing="true"] { animation: wani-arrive .35s ease-out; }
78
- .slide.active .decorations[data-moving="true"] .rabbit-position { animation: rabbit-advance .45s ease-in-out; }
79
- .slide.active .decorations[data-moving="true"] .rabbit-image { animation: rabbit-hop .45s ease-in-out; }
82
+ .slide.active .decorations[data-moving="true"] .rabbit-position { animation: rabbit-advance .45s linear; }
83
+ .slide.active .decorations[data-moving="true"] .rabbit-image { animation: rabbit-hop .45s linear; }
80
84
  @keyframes wani-arrive { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
81
85
  @keyframes rabbit-advance { from { left: calc((100% - 84px) * var(--rabbit-from)); } to { left: calc((100% - 84px) * var(--rabbit-progress)); } }
82
- @keyframes rabbit-hop { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
86
+ @keyframes rabbit-hop {
87
+ 0% { transform: translateY(0); animation-timing-function: cubic-bezier(.16, 1, .3, 1); }
88
+ 50% { transform: translateY(-60px); animation-timing-function: cubic-bezier(.333333, 0, .666667, .333333); }
89
+ 100% { transform: translateY(0); }
90
+ }
83
91
  @media (prefers-reduced-motion: reduce) { .decorations * { animation: none !important; } }
84
92
  #stage .slide:not(.active) { display: none; }
85
93
  .help { padding: 8px 24px; font-size: 11px; color: #718578; display: flex; gap: 22px; }
data/assets/wani.wasm CHANGED
Binary file
@@ -17,6 +17,8 @@ module Wani
17
17
  response = JS.global.fetch("/talk/slides.md").await
18
18
  raise "Markdown を読み込めません (HTTP #{response[:status].to_i})" unless response[:ok] == JS::True
19
19
  @presentation = Core::Parser.new.parse(response.text.await.to_s)
20
+ title = @presentation.slides.first.title
21
+ @document[:title] = title unless title.empty?
20
22
  @presentation.diagnostics.each { |item| warn("#{item.slide ? "スライド #{item.slide}: " : ""}#{item.message}") }
21
23
  @renderer = Renderer.new(@document, @presentation)
22
24
  @sections = @presentation.slides.map { |slide| @renderer.slide(slide) }
@@ -36,8 +38,6 @@ module Wani
36
38
  element("start-overlay")[:hidden] = false
37
39
  element("viewport")[:inert] = true
38
40
  element("start-presentation").focus
39
- else
40
- start_timer
41
41
  end
42
42
  element("status")[:textContent] = "準備できました"
43
43
  end
@@ -5,6 +5,7 @@ module Wani
5
5
  HEIGHT = 1080
6
6
  BODY_MIN = 16
7
7
  BODY_MAX = 160
8
+ COVER_BODY_SIZE = 48
8
9
  TITLE_MIN = 24
9
10
  TITLE_MAX = 220
10
11
  NO_WRAP_EMS = 20
@@ -60,8 +61,26 @@ module Wani
60
61
  body = section.querySelector(".body")
61
62
  prepare_wrapping(body)
62
63
  fit_images(body)
63
- fit_font(body, BODY_MIN, BODY_MAX)
64
- @warn.call("スライド #{number}: 本文が収まりません: #{body[:textContent].to_s[0, 120]}") unless fits?(body)
64
+ if section[:classList].contains("cover") == JS::True
65
+ body[:style][:fontSize] = "#{COVER_BODY_SIZE}px"
66
+ else
67
+ fit_font(body, BODY_MIN, BODY_MAX)
68
+ end
69
+ body_fits = if section[:classList].contains("cover") == JS::True
70
+ cover_body_fits?(body)
71
+ else
72
+ fits?(body)
73
+ end
74
+ @warn.call("スライド #{number}: 本文が収まりません: #{body[:textContent].to_s[0, 120]}") unless body_fits
75
+ if section[:classList].contains("cover") == JS::True && title[:style][:display].to_s != "none"
76
+ title_rect = title.getBoundingClientRect
77
+ overlap = body[:children].to_a.any? do |child|
78
+ rect = child.getBoundingClientRect
79
+ rect[:top].to_f < title_rect[:bottom].to_f && rect[:bottom].to_f > title_rect[:top].to_f &&
80
+ rect[:left].to_f < title_rect[:right].to_f && rect[:right].to_f > title_rect[:left].to_f
81
+ end
82
+ @warn.call("スライド #{number}: タイトルと本文が重なっています") if overlap
83
+ end
65
84
  footnote = section.querySelector(".page-footnote")
66
85
  if footnote[:scrollWidth].to_i > footnote[:clientWidth].to_i + 1
67
86
  @warn.call("スライド #{number}: 脚注が1行に収まりません。短くするか、詳細を本文へ移してください。")
@@ -119,6 +138,15 @@ module Wani
119
138
  node[:scrollWidth].to_i <= node[:clientWidth].to_i + 1 && node[:scrollHeight].to_i <= node[:clientHeight].to_i + 1
120
139
  end
121
140
 
141
+ def cover_body_fits?(body)
142
+ bounds = body.getBoundingClientRect
143
+ body[:scrollWidth].to_i <= body[:clientWidth].to_i + 1 && body[:children].to_a.all? do |child|
144
+ rect = child.getBoundingClientRect
145
+ rect[:top].to_f >= bounds[:top].to_f - 1 && rect[:bottom].to_f <= bounds[:bottom].to_f + 1 &&
146
+ rect[:left].to_f >= bounds[:left].to_f - 1 && rect[:right].to_f <= bounds[:right].to_f + 1
147
+ end
148
+ end
149
+
122
150
  def fit_font(node, minimum, maximum)
123
151
  low, high, best = minimum, maximum, minimum
124
152
  while low <= high
@@ -17,6 +17,7 @@ module Wani
17
17
  title[:style][:display] = "none"
18
18
  section[:className] = "slide titleless"
19
19
  end
20
+ section[:classList].add("cover") if slide.number == 1 && !notes
20
21
  body = child(section, "div", "body")
21
22
  body[:innerHTML] = notes ? slide.notes_html : slide.html
22
23
  resolve_links(body)
data/lib/wani/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Wani
2
- VERSION = "0.1.14"
2
+ VERSION = "0.1.15"
3
3
  end
data/templates/slides.md CHANGED
@@ -6,10 +6,6 @@ time_limit: 5
6
6
 
7
7
  Ruby と Markdown でプレゼンテーション。
8
8
 
9
- - 左右キー・Space で移動
10
- - F で全画面
11
- - N で発表者ノート
12
-
13
9
  <!-- page-footnote: Wani のサンプルへようこそ -->
14
10
 
15
11
  <!-- notes:
@@ -160,6 +156,7 @@ N キーで、このページの実際のノートを確認できます。
160
156
 
161
157
  # 発表と配布へ
162
158
 
159
+ - 左右キー・Space で移動し、F キーで全画面にする
163
160
  - N キーで、このページの発表者ノートを開く
164
161
  - 「PDF」でスライドを保存する
165
162
  - 「ノート付き PDF」で説明も一緒に配布する
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wani
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.14
4
+ version: 0.1.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - ledsun