cosensee 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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