cosensee 0.6.0 → 0.8.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +13 -2
  3. data/README.md +25 -7
  4. data/Rakefile +1 -1
  5. data/assets/js/search.js +42 -0
  6. data/assets/styles/input.css +3 -0
  7. data/exe/cosensee +36 -0
  8. data/lib/cosensee/api/page_data.rb +43 -0
  9. data/lib/cosensee/bracket_parser.rb +21 -16
  10. data/lib/cosensee/bracket_serializer.rb +3 -1
  11. data/lib/cosensee/cli/initializer.rb +64 -0
  12. data/lib/cosensee/cli/option.rb +65 -0
  13. data/lib/cosensee/cli/parser.rb +83 -0
  14. data/lib/cosensee/delegatable.rb +33 -0
  15. data/lib/cosensee/html_builder.rb +26 -17
  16. data/lib/cosensee/line_parser.rb +29 -16
  17. data/lib/cosensee/node/blank_bracket.rb +10 -0
  18. data/lib/cosensee/node/code.rb +20 -0
  19. data/lib/cosensee/node/codeblock.rb +32 -0
  20. data/lib/cosensee/node/command_line.rb +20 -0
  21. data/lib/cosensee/node/decorate_bracket.rb +23 -0
  22. data/lib/cosensee/node/double_bracket.rb +22 -0
  23. data/lib/cosensee/node/empty_bracket.rb +10 -0
  24. data/lib/cosensee/node/external_link_bracket.rb +10 -0
  25. data/lib/cosensee/node/formula_bracket.rb +10 -0
  26. data/lib/cosensee/node/gyazo_image_bracket.rb +10 -0
  27. data/lib/cosensee/node/hash_tag.rb +21 -0
  28. data/lib/cosensee/node/icon_bracket.rb +10 -0
  29. data/lib/cosensee/node/image_bracket.rb +10 -0
  30. data/lib/cosensee/node/indent.rb +27 -0
  31. data/lib/cosensee/node/internal_link_bracket.rb +10 -0
  32. data/lib/cosensee/node/link.rb +20 -0
  33. data/lib/cosensee/node/quote.rb +26 -0
  34. data/lib/cosensee/node/spotify_playlist_bracket.rb +10 -0
  35. data/lib/cosensee/node/text_bracket.rb +12 -0
  36. data/lib/cosensee/node/youtube_bracket.rb +10 -0
  37. data/lib/cosensee/page.rb +46 -16
  38. data/lib/cosensee/page_store.rb +25 -2
  39. data/lib/cosensee/parsed_line.rb +24 -6
  40. data/lib/cosensee/project.rb +21 -6
  41. data/lib/cosensee/render_class_findable.rb +13 -0
  42. data/lib/cosensee/tailwind_command.rb +24 -0
  43. data/lib/cosensee/tailwind_renderer/blank_bracket.rb +1 -1
  44. data/lib/cosensee/tailwind_renderer/code.rb +1 -1
  45. data/lib/cosensee/tailwind_renderer/codeblock.rb +22 -0
  46. data/lib/cosensee/tailwind_renderer/command_line.rb +1 -1
  47. data/lib/cosensee/tailwind_renderer/decorate_bracket.rb +1 -1
  48. data/lib/cosensee/tailwind_renderer/double_bracket.rb +2 -2
  49. data/lib/cosensee/tailwind_renderer/empty_bracket.rb +1 -1
  50. data/lib/cosensee/tailwind_renderer/external_link_bracket.rb +1 -1
  51. data/lib/cosensee/tailwind_renderer/formula_bracket.rb +1 -1
  52. data/lib/cosensee/tailwind_renderer/gyazo_image_bracket.rb +15 -0
  53. data/lib/cosensee/tailwind_renderer/hash_tag.rb +1 -1
  54. data/lib/cosensee/tailwind_renderer/icon_bracket.rb +4 -3
  55. data/lib/cosensee/tailwind_renderer/image_bracket.rb +3 -3
  56. data/lib/cosensee/tailwind_renderer/inline_svg_text.rb +37 -0
  57. data/lib/cosensee/tailwind_renderer/internal_link_bracket.rb +1 -1
  58. data/lib/cosensee/tailwind_renderer/link.rb +1 -1
  59. data/lib/cosensee/tailwind_renderer/page.rb +3 -18
  60. data/lib/cosensee/tailwind_renderer/parsed_line.rb +4 -3
  61. data/lib/cosensee/tailwind_renderer/quote.rb +2 -2
  62. data/lib/cosensee/tailwind_renderer/spotify_playlist_bracket.rb +11 -0
  63. data/lib/cosensee/tailwind_renderer/text_bracket.rb +2 -2
  64. data/lib/cosensee/tailwind_renderer/youtube_bracket.rb +11 -0
  65. data/lib/cosensee/tailwind_renderer.rb +6 -11
  66. data/lib/cosensee/user.rb +7 -1
  67. data/lib/cosensee/version.rb +1 -1
  68. data/lib/cosensee/web_content_generator.rb +77 -0
  69. data/lib/cosensee/web_server/static_file_handler.rb +43 -0
  70. data/lib/cosensee/web_server.rb +38 -0
  71. data/lib/cosensee.rb +45 -20
  72. data/templates/index.html.erb +49 -27
  73. data/templates/page.html.erb +85 -29
  74. metadata +114 -23
  75. data/lib/cosensee/blank_bracket.rb +0 -8
  76. data/lib/cosensee/code.rb +0 -18
  77. data/lib/cosensee/codeblock.rb +0 -18
  78. data/lib/cosensee/command_line.rb +0 -18
  79. data/lib/cosensee/decorate_bracket.rb +0 -16
  80. data/lib/cosensee/double_bracket.rb +0 -20
  81. data/lib/cosensee/empty_bracket.rb +0 -8
  82. data/lib/cosensee/external_link_bracket.rb +0 -8
  83. data/lib/cosensee/formula_bracket.rb +0 -8
  84. data/lib/cosensee/hash_tag.rb +0 -19
  85. data/lib/cosensee/icon_bracket.rb +0 -8
  86. data/lib/cosensee/image_bracket.rb +0 -8
  87. data/lib/cosensee/indent.rb +0 -25
  88. data/lib/cosensee/internal_link_bracket.rb +0 -8
  89. data/lib/cosensee/line.rb +0 -47
  90. data/lib/cosensee/link.rb +0 -18
  91. data/lib/cosensee/quote.rb +0 -22
  92. data/lib/cosensee/tailwind_renderer/codeblock_builder.rb +0 -37
  93. data/lib/cosensee/text_bracket.rb +0 -10
@@ -3,18 +3,20 @@
3
3
  module Cosensee
4
4
  # parse a line
5
5
  class ParsedLine
6
- def initialize(indent: nil, line_content: nil, content: [], rest: nil, parsed: false)
6
+ def initialize(indent: nil, line_content: nil, content: [], rest: nil, raw: '', parsed: false)
7
7
  @indent = indent
8
8
  @line_content = line_content
9
9
  @content = content
10
10
  @rest = rest
11
+ @raw = raw
11
12
  @parsed = parsed
12
13
  end
13
14
 
14
15
  attr_accessor :indent, :line_content, :content, :rest, :parsed
16
+ attr_reader :raw
15
17
 
16
18
  def codeblock?
17
- line_content.is_a?(Cosensee::Codeblock)
19
+ line_content.is_a?(Cosensee::Node::Codeblock)
18
20
  end
19
21
 
20
22
  def parsed?
@@ -25,10 +27,26 @@ module Cosensee
25
27
  !!rest
26
28
  end
27
29
 
30
+ def song_tagged?
31
+ content.any? { |elem| elem.is_a?(Node::HashTag) && elem.anchor == '楽曲' }
32
+ end
33
+
34
+ def first_image
35
+ if line_content?
36
+ line_content.is_a?(Node::Quote) && line_content.content.find { |elem| elem.is_a?(Node::ImageBracket) || elem.is_a?(Node::GyazoImageBracket) }
37
+ else
38
+ content.find { |elem| elem.is_a?(Node::ImageBracket) || elem.is_a?(Node::GyazoImageBracket) }
39
+ end
40
+ end
41
+
28
42
  def line_content?
29
43
  !!line_content
30
44
  end
31
45
 
46
+ def append_text(text:, raw_line:)
47
+ self.line_content = line_content.append_text(text:, raw_line:)
48
+ end
49
+
32
50
  def indent_level
33
51
  indent.level
34
52
  end
@@ -53,14 +71,14 @@ module Cosensee
53
71
  end
54
72
 
55
73
  def internal_links
56
- content.select { |c| c.is_a?(Cosensee::InternalLinkBracket) || c.is_a?(Cosensee::HashTag) }.map(&:anchor)
74
+ content.select { |c| c.is_a?(Cosensee::Node::InternalLinkBracket) || c.is_a?(Cosensee::Node::HashTag) }.map(&:anchor)
57
75
  end
58
76
 
59
- def raw
77
+ def to_s
60
78
  if line_content?
61
- "#{indent.raw}#{line_content.raw}"
79
+ line_content.to_s
62
80
  else
63
- "#{indent.raw}#{content.map(&:to_s).join}"
81
+ content.map(&:to_s).join
64
82
  end
65
83
  end
66
84
 
@@ -5,18 +5,20 @@ require 'json'
5
5
  module Cosensee
6
6
  # for Project
7
7
  class Project
8
- def self.parse(source)
8
+ extend Delegatable
9
+
10
+ def self.parse(source, renderer_class:)
9
11
  json = JSON.parse(source, symbolize_names: true)
10
12
  name, display_name, exported, users, pages = json.values_at(:name, :displayName, :exported, :users, :pages)
11
- Cosensee::Project.new(name:, display_name:, exported:, users:, pages:, source:)
13
+ Cosensee::Project.new(name:, display_name:, exported:, users:, pages:, source:, renderer_class:)
12
14
  end
13
15
 
14
- def self.parse_file(filename)
16
+ def self.parse_file(filename, renderer_class:)
15
17
  src = File.read(filename)
16
- parse(src)
18
+ parse(src, renderer_class:)
17
19
  end
18
20
 
19
- def initialize(name:, exported:, users:, pages:, source:, **kwargs)
21
+ def initialize(name:, exported:, users:, pages:, source:, renderer_class:, **kwargs)
20
22
  @name = name
21
23
  @display_name = if kwargs.keys.size == 1 && kwargs.key?(:display_name)
22
24
  kwargs[:display_name]
@@ -31,9 +33,22 @@ module Cosensee
31
33
  @source = source
32
34
 
33
35
  @page_store = PageStore.new(project: self)
36
+ @renderer_class = renderer_class
34
37
  end
35
38
 
36
- attr_reader :name, :display_name, :users, :pages, :exported, :source, :page_store
39
+ attr_reader :name, :display_name, :users, :pages, :exported, :source, :page_store, :renderer_class
40
+
41
+ delegate :orphan_page_titles, :dump_search_data, :find_page_by_title, to: :page_store
42
+
43
+ def sorted_pages_for_top
44
+ pinned_pages = page_store.pinned_pages
45
+ if pinned_pages.empty?
46
+ pages.sort_by(&:updated).reverse
47
+ else
48
+ unpinned_pages = pages - pinned_pages
49
+ pinned_pages + unpinned_pages.sort_by(&:updated).reverse
50
+ end
51
+ end
37
52
 
38
53
  def to_obj
39
54
  { name:, displayName: display_name, exported: exported.to_i, users:, pages: }
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ # provide `renderer_class` method
5
+ module RenderClassFindable
6
+ # ex. Cosensee::TailwindRenderer::Code for Cosensee::Node::Code
7
+ def find_renderer_class(content)
8
+ name = content.class.name.split('::').last
9
+
10
+ Cosensee::TailwindRenderer.const_get(name)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tailwindcss/ruby'
4
+
5
+ module Cosensee
6
+ # build TailwindCSS Command, based on https://github.com/rails/tailwindcss-rails
7
+ module TailwindCommand
8
+ def self.compile_command(output_dir: Cosensee::DEFAULT_OUTPUT_DIR, css_dir: DEFAULT_CSS_DIR, debug: false, **)
9
+ command = [
10
+ Tailwindcss::Ruby.executable(**),
11
+ '-i', File.join(__dir__, '../../assets/styles/input.css'),
12
+ '-o', File.join(output_dir, css_dir, 'tailwind.css'),
13
+ '-c', Cosensee::TAILWIND_CONFIG_FILE
14
+ ]
15
+
16
+ command << '--minify' unless debug
17
+
18
+ postcss_path = 'postcss.config.js'
19
+ command += ['--postcss', postcss_path.to_s] if File.exist?(postcss_path)
20
+
21
+ command
22
+ end
23
+ end
24
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- BlankBracket = Data.define(:content) do
5
+ BlankBracket = Data.define(:content, :project) do
6
6
  def render
7
7
  %(<span>#{content.blank}</span>)
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- Code = Data.define(:content) do
5
+ Code = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
7
 
8
8
  def render
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ class TailwindRenderer
5
+ # codeblock renderer
6
+ Codeblock = Data.define(:content, :project) do
7
+ include HtmlEncodable
8
+
9
+ def render
10
+ title = content.name
11
+
12
+ # !!! EXTENSION: if name ends with .svgtext, render as inline SVG text
13
+ return InlineSvgText.new(content:, project:).render if title.match?(/\.svgtext$/)
14
+
15
+ <<~HTML_BLOCK
16
+ <div class="bg-orange-300 text-gray-900 px-4 py-1 rounded-t-lg font-mono text-sm">#{title}</div>
17
+ <div class="px-4 bg-gray-300 text-gray-900 rounded-b-lg shadow-md"><pre class="overflow-x-auto"><code class="block font-mono text-sm leading-relaxed">#{escape_html(content.content)}</code></pre></div>
18
+ HTML_BLOCK
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,7 +4,7 @@ require 'json'
4
4
 
5
5
  module Cosensee
6
6
  class TailwindRenderer
7
- CommandLine = Data.define(:content) do
7
+ CommandLine = Data.define(:content, :project) do
8
8
  include HtmlEncodable
9
9
 
10
10
  def render
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- DecorateBracket = Data.define(:content) do
5
+ DecorateBracket = Data.define(:content, :project) do
6
6
  def render
7
7
  classes = []
8
8
  classes << font_sizes[content.font_size] if content.font_size
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- DoubleBracket = Data.define(:content) do
5
+ DoubleBracket = Data.define(:content, :project) do
6
6
  def render
7
- rendered_content = Cosensee::TailwindRenderer.new(content: content.content).render
7
+ rendered_content = Cosensee::TailwindRenderer.new(content: content.content, project:).render
8
8
 
9
9
  %(<span class="font-bold">#{rendered_content}</span>)
10
10
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- EmptyBracket = Data.define(:content) do
5
+ EmptyBracket = Data.define(:content, :project) do
6
6
  def render
7
7
  %(<span>[]</span>)
8
8
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- ExternalLinkBracket = Data.define(:content) do
5
+ ExternalLinkBracket = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
7
 
8
8
  def render
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- FormulaBracket = Data.define(:content) do
5
+ FormulaBracket = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
7
 
8
8
  def render
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ class TailwindRenderer
5
+ GyazoImageBracket = Data.define(:content, :project) do
6
+ def render
7
+ if content.link
8
+ %(<span><a href="#{content.link}"><img loading="lazy" src="https://gyazo.com/#{content.image_id}/raw" class="max-w-max max-h-80"></a></span>)
9
+ else
10
+ %(<span><img loading="lazy" src="https://gyazo.com/#{content.image_id}/raw" class="max-w-max max-h-80"></span>)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -4,7 +4,7 @@ require 'uri'
4
4
 
5
5
  module Cosensee
6
6
  class TailwindRenderer
7
- HashTag = Data.define(:content) do
7
+ HashTag = Data.define(:content, :project) do
8
8
  include LinkEncodable
9
9
  include HtmlEncodable
10
10
 
@@ -2,12 +2,13 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- IconBracket = Data.define(:content) do
5
+ IconBracket = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
7
 
8
8
  def render
9
- # XXX use icon image finder with project object
10
- %(<span>[icon:#{escape_html(content.icon_name)}]</span>)
9
+ page = project.find_page_by_title(content.icon_name)
10
+ icon_src = page&.first_image&.src
11
+ %(<img src="#{icon_src}" loading="lazy" alt="icon" class="inline-block h-5 w-5 align-middle">)
11
12
  end
12
13
  end
13
14
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- ImageBracket = Data.define(:content) do
5
+ ImageBracket = Data.define(:content, :project) do
6
6
  def render
7
7
  if content.link
8
- %(<span><a href="#{content.link}"><img src="#{content.src}" class="max-w-max max-h-80"></a></span>)
8
+ %(<span><a href="#{content.link}"><img src="#{content.src}" loading="lazy" class="max-w-max max-h-80"></a></span>)
9
9
  else
10
- %(<span><img src="#{content.src}" class="max-w-max max-h-80"></span>)
10
+ %(<span><img src="#{content.src}" loading="lazy" class="max-w-max max-h-80"></span>)
11
11
  end
12
12
  end
13
13
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ class TailwindRenderer
5
+ # extended renderer for inline SVG text
6
+ InlineSvgText = Data.define(:content, :project) do
7
+ include HtmlEncodable
8
+
9
+ def render
10
+ output = <<~SVG_TEXT_HEADER
11
+ <svg style="inherit" xmlns="http://www.w3.org/2000/svg" class="svg-text">
12
+ <style>
13
+ .text { -ms-user-select: none; -webkit-user-select: none; user-select: none; }
14
+ @media print { .svg-text { display: none !important; } text { display: none !important; } }
15
+ </style>
16
+ <text x="0" y="0" font-size="inherit" class="text" fill="break">
17
+ SVG_TEXT_HEADER
18
+
19
+ content.content.lines.each do |line|
20
+ tspan_text = if line.strip.empty?
21
+ '&#xA0;'
22
+ else
23
+ escape_html(line.chomp)
24
+ end
25
+ output << %(<tspan x="0" dy="1.4em">#{tspan_text}</tspan>\n)
26
+ end
27
+
28
+ output << <<~SVG_TEXT_FOOTER
29
+ </text>
30
+ </svg>
31
+ SVG_TEXT_FOOTER
32
+
33
+ output
34
+ end
35
+ end
36
+ end
37
+ end
@@ -4,7 +4,7 @@ require 'uri'
4
4
 
5
5
  module Cosensee
6
6
  class TailwindRenderer
7
- InternalLinkBracket = Data.define(:content) do
7
+ InternalLinkBracket = Data.define(:content, :project) do
8
8
  include HtmlEncodable
9
9
 
10
10
  def render
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- Link = Data.define(:content) do
5
+ Link = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
7
 
8
8
  def render
@@ -2,26 +2,11 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- Page = Data.define(:content) do
5
+ Page = Data.define(:content, :project) do
6
6
  def render
7
- current_block_builder = nil
8
-
9
7
  page = content
10
- rendered_lines = []
11
-
12
- page.parsed_lines.each do |parsed_line|
13
- if current_block_builder
14
- if current_block_builder.continued_line?(parsed_line)
15
- current_block_builder.append(parsed_line)
16
- else
17
- rendered_lines << current_block_builder.render
18
- rendered_lines << ParsedLine.new(parsed_line).render
19
- end
20
- elsif parsed_line.codeblock?
21
- current_block_builder = CodeblockBuilder.new(parsed_line)
22
- else
23
- rendered_lines << ParsedLine.new(parsed_line).render
24
- end
8
+ rendered_lines = page.parsed_lines.map do |parsed_line|
9
+ ParsedLine.new(parsed_line, project).render
25
10
  end
26
11
 
27
12
  rendered_lines.join
@@ -2,18 +2,19 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- ParsedLine = Data.define(:content) do
5
+ ParsedLine = Data.define(:content, :project) do
6
6
  include HtmlEncodable
7
+ include RenderClassFindable
7
8
 
8
9
  def render
9
10
  result = if content.line_content?
10
- TailwindRenderer.new(content: content.line_content).render
11
+ find_renderer_class(content.line_content).new(content: content.line_content, project:).render
11
12
  else
12
13
  content.content.map do |c|
13
14
  if c.is_a?(String)
14
15
  escape_html(c)
15
16
  else
16
- TailwindRenderer.new(content: c).render
17
+ TailwindRenderer.new(content: c, project:).render
17
18
  end
18
19
  end.join
19
20
  end
@@ -4,9 +4,9 @@ require 'json'
4
4
 
5
5
  module Cosensee
6
6
  class TailwindRenderer
7
- Quote = Data.define(:content) do
7
+ Quote = Data.define(:content, :project) do
8
8
  def render
9
- code = Cosensee::TailwindRenderer.new(content: content.content).render
9
+ code = Cosensee::TailwindRenderer.new(content: content.content, project:).render
10
10
  %(<blockquote class="border-l-4 border-gray-300 bg-gray-100 px-4 text-gray-800">#{code}</blockquote>)
11
11
  end
12
12
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ class TailwindRenderer
5
+ SpotifyPlaylistBracket = Data.define(:content, :project) do
6
+ def render
7
+ %(<iframe src="https://open.spotify.com/embed/playlist/#{content.playlist_id}" class="w-full h-96" frameborder="0" allowtransparency="true" allow="encrypted-media"></iframe>)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -2,9 +2,9 @@
2
2
 
3
3
  module Cosensee
4
4
  class TailwindRenderer
5
- TextBracket = Data.define(:content) do
5
+ TextBracket = Data.define(:content, :project) do
6
6
  def render
7
- rendered = TailwindRenderer.new(content: content.content).render
7
+ rendered = TailwindRenderer.new(content: content.content, project:).render
8
8
  %(<span>[#{rendered}]</span>)
9
9
  end
10
10
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ class TailwindRenderer
5
+ YoutubeBracket = Data.define(:content, :project) do
6
+ def render
7
+ %(<iframe class="w-full aspect-video px-8" src="https://www.youtube.com/embed/#{content.video_id}?autoplay=0" type="text/html" allowfullscreen="" scrolling="no" allow="encrypted-media"></iframe>)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,13 +4,15 @@ module Cosensee
4
4
  # convert into html
5
5
  class TailwindRenderer
6
6
  include HtmlEncodable
7
+ include RenderClassFindable
7
8
 
8
9
  # content is Cosensee objects or an array of them
9
- def initialize(content:)
10
+ def initialize(content:, project:)
10
11
  @content = content
12
+ @project = project
11
13
  end
12
14
 
13
- attr_reader :content
15
+ attr_reader :content, :project
14
16
 
15
17
  def render
16
18
  if content.is_a?(Array)
@@ -18,19 +20,12 @@ module Cosensee
18
20
  if c.is_a?(String)
19
21
  escape_html(c)
20
22
  else
21
- renderer_class(c).new(content: c).render
23
+ find_renderer_class(c).new(content: c, project:).render
22
24
  end
23
25
  end.join
24
26
  else
25
- renderer_class(content).new(content:).render
27
+ find_renderer_class(content).new(content:, project:).render
26
28
  end
27
29
  end
28
-
29
- # ex. Cosensee::TailwindRenderer::Code for Cosensee::Code
30
- def renderer_class(content)
31
- name = content.class.name.split('::').last
32
-
33
- Cosensee::TailwindRenderer.const_get(name)
34
- end
35
30
  end
36
31
  end
data/lib/cosensee/user.rb CHANGED
@@ -6,7 +6,13 @@ module Cosensee
6
6
  # for User
7
7
  User = Data.define(:id, :name, :display_name, :email) do
8
8
  def self.from_array(users_args)
9
- users_args.map { |args| new(**args) }
9
+ users_args.map do |args|
10
+ if args.is_a?(Cosensee::User)
11
+ args
12
+ else
13
+ new(**args)
14
+ end
15
+ end
10
16
  end
11
17
 
12
18
  # allow both `:display_key` and `:displayKey`
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Cosensee
4
- VERSION = '0.6.0'
4
+ VERSION = '0.8.0'
5
5
  end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cosensee
4
+ # convert project file to web content
5
+ class WebContentGenerator
6
+ extend Cosensee::Delegatable
7
+
8
+ class Error < StandardError; end
9
+
10
+ SEARCH_DATA_PATH = 'search.json'
11
+
12
+ attr_reader :option, :logger, :sid, :skip_tailwind_execution, :renderer_class
13
+
14
+ def initialize(option:, renderer_class:, logger:, sid:)
15
+ @option = option
16
+ @renderer_class = renderer_class
17
+ @logger = logger
18
+ @sid = sid
19
+ @skip_tailwind_execution = option.skip_tailwind_execution?
20
+ end
21
+
22
+ def generate
23
+ raise Error, 'Filename is missing.' unless filename
24
+
25
+ download_page_data(sid) if option.remote?
26
+
27
+ raise Error, "File not found - #{filename}" unless File.exist?(filename)
28
+
29
+ logger.info "Processing file: #{filename}"
30
+ project = Cosensee::Project.parse_file(filename, renderer_class:)
31
+ Cosensee::HtmlBuilder.new(project, output_dir: option.output_dir, base_url: option.base_url).build_all(clean: option.clean?)
32
+ logger.info "Build all files into #{option.output_dir}."
33
+
34
+ copy_js_files
35
+
36
+ execute_tailwind unless skip_tailwind_execution
37
+
38
+ dump_search_data(project)
39
+ end
40
+
41
+ def execute_tailwind
42
+ FileUtils.mkdir_p(File.join(option.output_dir, option.css_dir))
43
+ command = Cosensee::TailwindCommand.compile_command(output_dir: option.output_dir, css_dir: option.css_dir, debug: false)
44
+ logger.info "Processing TailwindCSS: #{command.inspect}"
45
+ system(*command, exception: true)
46
+ end
47
+
48
+ def dump_search_data(project)
49
+ data = project.dump_search_data
50
+ File.write(File.join(option.output_dir, SEARCH_DATA_PATH), data.to_json)
51
+ end
52
+
53
+ private
54
+
55
+ delegate :filename, :project_name, to: :option
56
+
57
+ def download_page_data(sid)
58
+ raise Error, 'You must set CONNECT_SID as environment variable.' unless sid
59
+
60
+ logger.info 'Retrieving file from remote API...'
61
+ Cosensee::Api::PageData.new.download(
62
+ project_name:,
63
+ sid:,
64
+ filename:
65
+ )
66
+ logger.info "File retrieved and saved as: #{filename}"
67
+ end
68
+
69
+ def copy_js_files
70
+ FileUtils.mkdir_p(File.join(option.output_dir, 'js'))
71
+ Dir.glob("#{__dir__}/../../assets/js/*.js") do |path|
72
+ js_file = File.basename(path)
73
+ FileUtils.cp(path, File.join(option.output_dir, 'js', js_file))
74
+ end
75
+ end
76
+ end
77
+ end