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
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_mime'
4
+ require 'uri'
5
+
6
+ module Cosensee
7
+ class WebServer
8
+ # Rack application for Falcon that serves static files from a directory.
9
+ class StaticFileHandler
10
+ def initialize(dir:, logger:)
11
+ @dir = dir
12
+ @logger = logger
13
+ end
14
+
15
+ attr_reader :dir, :logger
16
+
17
+ def call(env)
18
+ path_info = env['PATH_INFO']
19
+ logger.info("Request path: #{env['PATH_INFO']}")
20
+ path_info = if path_info.start_with?('/')
21
+ "/#{URI.decode_www_form_component(path_info.slice(1..-1))}"
22
+ else
23
+ URI.decode_www_form_component(path_info)
24
+ end
25
+ path = File.join(dir, path_info)
26
+ path = File.join(path, 'index.html') if File.directory?(path)
27
+
28
+ if File.exist?(path) && !File.directory?(path)
29
+ content = File.read(path)
30
+ content_type = MiniMime.lookup_by_filename(path).content_type
31
+ content_length = content.bytesize.to_s
32
+
33
+ logger.info("Response size: #{content_length}")
34
+
35
+ [200, { 'Content-Type' => content_type, 'Content-Length' => content_length }, [content]]
36
+ else
37
+ logger.info("Error File not found path: #{env['PATH_INFO']}")
38
+ [404, { 'Content-Type' => 'text/plain' }, ["File not found: #{env['PATH_INFO']}"]]
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'async'
4
+ require 'async/http/endpoint'
5
+ require 'falcon'
6
+
7
+ module Cosensee
8
+ # Web server for serving static files
9
+ class WebServer
10
+ def initialize(dir:, server_url:, logger:)
11
+ @dir = dir
12
+ @server_url = server_url
13
+ @logger = logger
14
+
15
+ endpoint = Async::HTTP::Endpoint.parse(server_url)
16
+ handler = StaticFileHandler.new(dir:, logger:)
17
+ app = Falcon::Server.middleware(handler)
18
+ @server = Falcon::Server.new(app, endpoint)
19
+ end
20
+
21
+ def start
22
+ Async do |task|
23
+ @logger.info("Serving files from #{File.expand_path(@dir)} at #{@server_url}")
24
+
25
+ Signal.trap('INT') do
26
+ @logger.info("\nShutting down the server...")
27
+ task.stop
28
+ end
29
+
30
+ @server.run
31
+ end
32
+ rescue Interrupt
33
+ @logger.warn("\nServer interrupted. Exiting.")
34
+ ensure
35
+ @logger.info('Cleanup complete.')
36
+ end
37
+ end
38
+ end
data/lib/cosensee.rb CHANGED
@@ -5,50 +5,75 @@ require_relative 'cosensee/error'
5
5
  require_relative 'cosensee/bracket_serializer'
6
6
  require_relative 'cosensee/html_encodable'
7
7
  require_relative 'cosensee/link_encodable'
8
+ require_relative 'cosensee/delegatable'
8
9
 
9
- require_relative 'cosensee/blank_bracket'
10
+ require_relative 'cosensee/api/page_data'
10
11
  require_relative 'cosensee/bracket_parser'
11
- require_relative 'cosensee/code'
12
- require_relative 'cosensee/codeblock'
13
- require_relative 'cosensee/command_line'
14
- require_relative 'cosensee/decorate_bracket'
15
- require_relative 'cosensee/double_bracket'
16
- require_relative 'cosensee/empty_bracket'
17
- require_relative 'cosensee/external_link_bracket'
18
- require_relative 'cosensee/formula_bracket'
19
- require_relative 'cosensee/hash_tag'
12
+ require_relative 'cosensee/cli/option'
13
+ require_relative 'cosensee/cli/parser'
14
+ require_relative 'cosensee/cli/initializer'
20
15
  require_relative 'cosensee/html_builder'
21
- require_relative 'cosensee/icon_bracket'
22
- require_relative 'cosensee/image_bracket'
23
- require_relative 'cosensee/indent'
24
- require_relative 'cosensee/internal_link_bracket'
25
16
  require_relative 'cosensee/line_parser'
26
- require_relative 'cosensee/line'
27
- require_relative 'cosensee/link'
28
- require_relative 'cosensee/page'
17
+ require_relative 'cosensee/node/blank_bracket'
18
+ require_relative 'cosensee/node/code'
19
+ require_relative 'cosensee/node/codeblock'
20
+ require_relative 'cosensee/node/command_line'
21
+ require_relative 'cosensee/node/decorate_bracket'
22
+ require_relative 'cosensee/node/double_bracket'
23
+ require_relative 'cosensee/node/empty_bracket'
24
+ require_relative 'cosensee/node/external_link_bracket'
25
+ require_relative 'cosensee/node/formula_bracket'
26
+ require_relative 'cosensee/node/gyazo_image_bracket'
27
+ require_relative 'cosensee/node/hash_tag'
28
+ require_relative 'cosensee/node/icon_bracket'
29
+ require_relative 'cosensee/node/image_bracket'
30
+ require_relative 'cosensee/node/indent'
31
+ require_relative 'cosensee/node/internal_link_bracket'
32
+ require_relative 'cosensee/node/link'
33
+ require_relative 'cosensee/node/quote'
34
+ require_relative 'cosensee/node/spotify_playlist_bracket'
35
+ require_relative 'cosensee/node/text_bracket'
36
+ require_relative 'cosensee/node/youtube_bracket'
29
37
  require_relative 'cosensee/page_store'
38
+ require_relative 'cosensee/page'
30
39
  require_relative 'cosensee/parsed_bracket'
31
40
  require_relative 'cosensee/parsed_line'
32
41
  require_relative 'cosensee/project'
33
- require_relative 'cosensee/quote'
42
+ require_relative 'cosensee/render_class_findable'
43
+ require_relative 'cosensee/tailwind_command'
34
44
  require_relative 'cosensee/tailwind_renderer'
35
45
  require_relative 'cosensee/tailwind_renderer/blank_bracket'
36
46
  require_relative 'cosensee/tailwind_renderer/code'
37
- require_relative 'cosensee/tailwind_renderer/codeblock_builder'
47
+ require_relative 'cosensee/tailwind_renderer/codeblock'
38
48
  require_relative 'cosensee/tailwind_renderer/command_line'
39
49
  require_relative 'cosensee/tailwind_renderer/decorate_bracket'
40
50
  require_relative 'cosensee/tailwind_renderer/double_bracket'
41
51
  require_relative 'cosensee/tailwind_renderer/empty_bracket'
42
52
  require_relative 'cosensee/tailwind_renderer/external_link_bracket'
43
53
  require_relative 'cosensee/tailwind_renderer/formula_bracket'
54
+ require_relative 'cosensee/tailwind_renderer/gyazo_image_bracket'
44
55
  require_relative 'cosensee/tailwind_renderer/hash_tag'
45
56
  require_relative 'cosensee/tailwind_renderer/icon_bracket'
57
+ require_relative 'cosensee/tailwind_renderer/inline_svg_text'
46
58
  require_relative 'cosensee/tailwind_renderer/image_bracket'
47
59
  require_relative 'cosensee/tailwind_renderer/internal_link_bracket'
48
60
  require_relative 'cosensee/tailwind_renderer/link'
49
61
  require_relative 'cosensee/tailwind_renderer/page'
50
62
  require_relative 'cosensee/tailwind_renderer/parsed_line'
51
63
  require_relative 'cosensee/tailwind_renderer/quote'
64
+ require_relative 'cosensee/tailwind_renderer/spotify_playlist_bracket'
52
65
  require_relative 'cosensee/tailwind_renderer/text_bracket'
53
- require_relative 'cosensee/text_bracket'
66
+ require_relative 'cosensee/tailwind_renderer/youtube_bracket'
54
67
  require_relative 'cosensee/user'
68
+ require_relative 'cosensee/web_content_generator'
69
+ require_relative 'cosensee/web_server'
70
+ require_relative 'cosensee/web_server/static_file_handler'
71
+
72
+ module Cosensee
73
+ MAX_SUMMARY_LINE = 10
74
+ MAX_SUMMARY_TEXT_SIZE = 300
75
+ DEFAULT_OUTPUT_DIR = './dist'
76
+ DEFAULT_CSS_DIR = 'css'
77
+ DEFAULT_PORT = '1212'
78
+ TAILWIND_CONFIG_FILE = 'tailwind.config.js'
79
+ end
@@ -1,36 +1,58 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <meta charset="utf-8"/>
5
- <meta name="referrer" content="same-origin"/>
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"/>
7
- <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/>
4
+ <meta charset="utf-8">
5
+ <meta name="referrer" content="same-origin">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
7
+ <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
8
8
  <title><%= project.display_name %></title>
9
- <script src="https://cdn.tailwindcss.com"></script>
10
- <style>
11
- p.pagetitle {
12
- display: -webkit-box;
13
- -webkit-line-clamp: 3;
14
- -webkit-box-orient: vertical;
15
- }
16
- </style>
9
+ <link href="./<%= css_dir %>/tailwind.css" rel="stylesheet">
10
+ <script type="module">
11
+ import { createSearch } from '/js/search.js';
12
+ document.addEventListener('alpine:init', () => Alpine.data('searchComponent', createSearch));
13
+ </script>
14
+ <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js" defer></script>
17
15
  </head>
18
16
  <body class="bg-gray-100 p-4">
19
- <div class="max-w-screen-xl mx-auto">
20
- <h1 class="text-xl mb-4"><%= project.display_name %> ページ一覧</h1>
21
- <div class="grid grid-cols-8 gap-4">
22
- <% project.pages.each do |page|%>
23
- <div class="bg-white shadow-md rounded-md aspect-square flex items-start justify-center p-2 overflow-hidden">
24
- <div class="break-words text-xs w-full">
25
- <a href="<%= page.link_path %>">
26
- <p class="pagetitle font-bold overflow-hidden"><%= page.title %></p>
27
- </a>
28
- <a href="<%= page.link_path %>">
29
- <% page.body_lines.each do |line| %>
30
- <p class="inline text-xs"><%= line.to_s %></p>
31
- <% end %>
32
- </a>
33
- </div>
17
+ <div class="max-w-screen-xl mx-auto" x-data="searchComponent">
18
+ <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between my-4">
19
+ <h1 class="text-2xl font-extralight">
20
+ <%= project.display_name %> <span class="text-gray-400">(all pages)</span>
21
+ </h1>
22
+ <form action="/" method="GET" class="mt-2 sm:mt-0 w-full sm:w-[32rem] relative">
23
+ <div class="flex">
24
+ <input
25
+ id="searchInput"
26
+ type="text"
27
+ placeholder="Search..."
28
+ x-model="query"
29
+ @input="search"
30
+ @keydown.escape="clearResults"
31
+ @click="restoreResults"
32
+ class="flex-grow px-4 py-2 border rounded-l-md shadow-sm focus:outline-none focus:ring focus:ring-blue-300">
33
+ </div>
34
+ <ul id="resultsList" class="absolute bg-white shadow-md mt-2 w-full rounded-md z-10" x-show="results.length > 0" @click.away="results = []">
35
+ <template x-for="result in results" :key="result.item.link">
36
+ <li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">
37
+ <a :href="result.item.link" class="block">
38
+ <span class="block text-sm font-medium text-ellipsis whitespace-nowrap overflow-hidden" x-text="result.item.title.slice(0, 50)"></span>
39
+ <p class="text-xs text-gray-500 text-ellipsis whitespace-nowrap overflow-hidden" x-text="result.item.summary.slice(0, 50)"></p>
40
+ </a>
41
+ </li>
42
+ </template>
43
+ </ul>
44
+ </form>
45
+ </div>
46
+
47
+ <div class="flex flex-wrap gap-3">
48
+ <% project.sorted_pages_for_top.each do |page| %>
49
+ <div class="bg-white shadow-md rounded-md aspect-square flex items-start justify-center p-2 overflow-hidden hover:bg-gray-200 border-t-4 border-gray-300 basis-[148px] max-w-[148px]">
50
+ <div class="break-words text-xs w-full">
51
+ <a href="<%= page.link_path %>" class="block ">
52
+ <p class="line-clamp-3 font-semibold"><%= page.title %></p>
53
+ <p class="inline text-xs leading-relaxed text-gray-500 overflow-hidden"><%== page.summary %></p>
54
+ </a>
55
+ </div>
34
56
  </div>
35
57
  <% end %>
36
58
  </div>
@@ -1,35 +1,73 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <meta charset="utf-8"/>
5
- <meta name="referrer" content="same-origin"/>
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"/>
7
- <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"/>
8
- <meta name="format-detection" content="email=no,telephone=no,address=no,date=no"/>
9
- <title><%= title %></title>
10
- <script src="https://cdn.tailwindcss.com"></script>
4
+ <meta charset="utf-8">
5
+ <meta name="referrer" content="same-origin">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
7
+ <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
8
+ <meta name="format-detection" content="email=no,telephone=no,address=no,date=no">
9
+ <title><%= title %> | <%= project.name %></title>
10
+ <link href="./<%= css_dir %>/tailwind.css" rel="stylesheet">
11
+ <script type="module">
12
+ import { createSearch } from '/js/search.js';
13
+ document.addEventListener('alpine:init', () => Alpine.data('searchComponent', createSearch));
14
+ </script>
15
+ <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.8/dist/cdn.min.js" defer></script>
11
16
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.min.css" integrity="sha384-veTAhWILPOotXm+kbR5uY7dRamYLJf58I7P+hJhjeuc7hsMAkJHTsPahAl0hBST0" crossorigin="anonymous">
12
17
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.min.js" integrity="sha384-v6mkHYHfY/4BWq54f7lQAdtIsoZZIByznQ3ZqN38OL4KCsrxo31SLlPiak7cj/Mg" crossorigin="anonymous"></script>
13
18
  <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/contrib/auto-render.min.js" integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh" crossorigin="anonymous"></script>
14
-
15
- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i,700,700i|Roboto:400,400i,500,500i,700,700i"/>
16
- <style>
17
- p.pagetitle {
18
- display: -webkit-box;
19
- -webkit-line-clamp: 3;
20
- -webkit-box-orient: vertical;
21
- }
22
- </style>
19
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,600,600i,700,700i|Roboto:400,400i,500,500i,700,700i">
20
+ <meta property="og:title" content="<%= title %>">
21
+ <meta property="og:type" content="article">
22
+ <meta property="og:url" content="<%= page.full_url(base_url:) %>">
23
+ <% if page.ogp_image_url %>
24
+ <meta property="og:image" content="<%= page.ogp_image_url %>">
25
+ <% end %>
26
+ <meta property="og:description" content="<%= page.description %>">
27
+ <meta property="og:site_name" content="<%= project.display_name %>">
28
+ <meta property="og:locale" content="ja_JP">
29
+ <meta name="twitter:card" content="summary">
30
+ <meta name="twitter:title" content="<%= title %>">
31
+ <meta name="twitter:description" content="<%= page.description %>">
32
+ <% if page.ogp_image_url %>
33
+ <meta name="twitter:image" content="<%= page.ogp_image_url %>">
34
+ <% end %>
23
35
  </head>
24
36
  <body class="bg-slate-100">
25
- <div class="max-w-screen-lg mx-auto p-4 m-4">
26
- <nav>
27
- <a href="/"><%= project.name %></a>
28
- </nav>
29
- <div class="max-w-screen bg-white">
30
- <h1 class="text-3xl py-4 my-4"><%= title %></h1>
31
- <div>
32
- <%= page&.to_html %>
37
+ <div class="max-w-screen-lg mx-auto p-4">
38
+ <div class="flex flex-col sm:flex-row sm:items-center sm:justify-between my-4" x-data="searchComponent">
39
+ <nav class="my-4 font-extralight text-2xl">
40
+ <span id="project-name"><a href="/"><%= project.name %></a></span>
41
+ </nav>
42
+ <form action="/" method="GET" class="mt-2 sm:mt-0 w-full sm:w-[32rem] relative">
43
+ <div class="flex">
44
+ <input
45
+ id="searchInput"
46
+ type="text"
47
+ placeholder="Search..."
48
+ x-model="query"
49
+ @input="search"
50
+ @keydown.escape="clearResults"
51
+ @click="restoreResults"
52
+ class="flex-grow px-4 py-2 border rounded-l-md shadow-sm focus:outline-none focus:ring focus:ring-blue-300">
53
+ </div>
54
+ <ul id="resultsList" class="absolute bg-white shadow-md mt-2 w-full rounded-md z-10" x-show="results.length > 0" @click.away="results = []">
55
+ <template x-for="result in results" :key="result.item.link">
56
+ <li class="px-4 py-2 hover:bg-gray-100 cursor-pointer">
57
+ <a :href="result.item.link" class="block">
58
+ <span class="block text-sm font-medium text-ellipsis whitespace-nowrap overflow-hidden" x-text="result.item.title.slice(0, 50)"></span>
59
+ <p class="text-xs text-gray-500 text-ellipsis whitespace-nowrap overflow-hidden" x-text="result.item.summary.slice(0, 50)"></p>
60
+ </a>
61
+ </li>
62
+ </template>
63
+ </ul>
64
+ </form>
65
+ </div>
66
+
67
+ <div class="max-w-screen bg-white p-4">
68
+ <h1 class="text-3xl mb-8"><%= title %></h1>
69
+ <div class="leading-7">
70
+ <%== page&.to_html(project:) %>
33
71
  </div>
34
72
  </div>
35
73
 
@@ -39,16 +77,14 @@
39
77
  Links<br>🔗
40
78
  </div>
41
79
  </div>
42
- <% project.page_store.find_link_pages_by_title(title).each do |page|%>
80
+ <% project.page_store.find_link_pages_by_title(title).each do |page| %>
43
81
  <div class="bg-white shadow-md rounded-md aspect-square flex items-start justify-center p-2 overflow-hidden">
44
82
  <div class="break-words text-xs w-full">
45
83
  <a href="<%= page.link_path %>">
46
- <p class="pagetitle font-bold overflow-hidden"><%= page.title %></p>
84
+ <p class="line-clamp-3 font-semibold"><%= page.title %></p>
47
85
  </a>
48
86
  <a href="<%= page.link_path %>">
49
- <% page.body_lines.each do |line| %>
50
- <p class="inline text-xs"><%= line.to_s %></p>
51
- <% end %>
87
+ <p class="inline text-xs"><%== page.summary %></p>
52
88
  </a>
53
89
  </div>
54
90
  </div>
@@ -56,4 +92,24 @@
56
92
  </div>
57
93
  </div>
58
94
  </body>
95
+ <script>
96
+ document.addEventListener('keydown', function(event) {
97
+ if (window.location.hostname === 'localhost' && event.key === 'o' && event.ctrlKey) {
98
+ event.preventDefault();
99
+ const projectName = document.querySelector('#project-name');
100
+ if (projectName) {
101
+ const currentPath = window.location.pathname.replace('.html', '');
102
+ const newUrl = `https://scrapbox.io/${projectName.textContent}${currentPath}`;
103
+ window.open(newUrl, 'scrapbox');
104
+ }
105
+ }
106
+ });
107
+ const padding = 20;
108
+ for (const svg of document.querySelectorAll("svg.svg-text")) {
109
+ const text = svg.querySelector("text.text");
110
+ const bbox = text.getBBox();
111
+ svg.setAttribute("width", bbox.width);
112
+ svg.setAttribute("height", bbox.height + padding);
113
+ };
114
+ </script>
59
115
  </html>
metadata CHANGED
@@ -1,14 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cosensee
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - takahashim
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-05 00:00:00.000000000 Z
10
+ date: 2025-01-18 00:00:00.000000000 Z
11
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: console
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: dotenv
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
12
40
  - !ruby/object:Gem::Dependency
13
41
  name: erubi
14
42
  requirement: !ruby/object:Gem::Requirement
@@ -23,6 +51,48 @@ dependencies:
23
51
  - - ">="
24
52
  - !ruby/object:Gem::Version
25
53
  version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: falcon
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: mini_mime
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: tailwindcss-ruby
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
26
96
  - !ruby/object:Gem::Dependency
27
97
  name: tilt
28
98
  requirement: !ruby/object:Gem::Requirement
@@ -37,10 +107,12 @@ dependencies:
37
107
  - - ">="
38
108
  - !ruby/object:Gem::Version
39
109
  version: '0'
40
- description: Cosense (Scrapbox) parser and renderer
110
+ description: Cosensee is a tool that reads JSON data from Cosense (formerly Scrapbox)
111
+ and outputs it in HTML format.
41
112
  email:
42
113
  - takahashimm@gmail.com
43
- executables: []
114
+ executables:
115
+ - cosensee
44
116
  extensions: []
45
117
  extra_rdoc_files: []
46
118
  files:
@@ -49,58 +121,77 @@ files:
49
121
  - ".rubocop_todo.yml"
50
122
  - README.md
51
123
  - Rakefile
124
+ - assets/js/search.js
125
+ - assets/styles/input.css
126
+ - exe/cosensee
52
127
  - lib/cosensee.rb
53
- - lib/cosensee/blank_bracket.rb
128
+ - lib/cosensee/api/page_data.rb
54
129
  - lib/cosensee/bracket_parser.rb
55
130
  - lib/cosensee/bracket_serializer.rb
56
- - lib/cosensee/code.rb
57
- - lib/cosensee/codeblock.rb
58
- - lib/cosensee/command_line.rb
59
- - lib/cosensee/decorate_bracket.rb
60
- - lib/cosensee/double_bracket.rb
61
- - lib/cosensee/empty_bracket.rb
131
+ - lib/cosensee/cli/initializer.rb
132
+ - lib/cosensee/cli/option.rb
133
+ - lib/cosensee/cli/parser.rb
134
+ - lib/cosensee/delegatable.rb
62
135
  - lib/cosensee/error.rb
63
- - lib/cosensee/external_link_bracket.rb
64
- - lib/cosensee/formula_bracket.rb
65
- - lib/cosensee/hash_tag.rb
66
136
  - lib/cosensee/html_builder.rb
67
137
  - lib/cosensee/html_encodable.rb
68
- - lib/cosensee/icon_bracket.rb
69
- - lib/cosensee/image_bracket.rb
70
- - lib/cosensee/indent.rb
71
- - lib/cosensee/internal_link_bracket.rb
72
- - lib/cosensee/line.rb
73
138
  - lib/cosensee/line_parser.rb
74
- - lib/cosensee/link.rb
75
139
  - lib/cosensee/link_encodable.rb
140
+ - lib/cosensee/node/blank_bracket.rb
141
+ - lib/cosensee/node/code.rb
142
+ - lib/cosensee/node/codeblock.rb
143
+ - lib/cosensee/node/command_line.rb
144
+ - lib/cosensee/node/decorate_bracket.rb
145
+ - lib/cosensee/node/double_bracket.rb
146
+ - lib/cosensee/node/empty_bracket.rb
147
+ - lib/cosensee/node/external_link_bracket.rb
148
+ - lib/cosensee/node/formula_bracket.rb
149
+ - lib/cosensee/node/gyazo_image_bracket.rb
150
+ - lib/cosensee/node/hash_tag.rb
151
+ - lib/cosensee/node/icon_bracket.rb
152
+ - lib/cosensee/node/image_bracket.rb
153
+ - lib/cosensee/node/indent.rb
154
+ - lib/cosensee/node/internal_link_bracket.rb
155
+ - lib/cosensee/node/link.rb
156
+ - lib/cosensee/node/quote.rb
157
+ - lib/cosensee/node/spotify_playlist_bracket.rb
158
+ - lib/cosensee/node/text_bracket.rb
159
+ - lib/cosensee/node/youtube_bracket.rb
76
160
  - lib/cosensee/page.rb
77
161
  - lib/cosensee/page_store.rb
78
162
  - lib/cosensee/parsed_bracket.rb
79
163
  - lib/cosensee/parsed_line.rb
80
164
  - lib/cosensee/project.rb
81
- - lib/cosensee/quote.rb
165
+ - lib/cosensee/render_class_findable.rb
166
+ - lib/cosensee/tailwind_command.rb
82
167
  - lib/cosensee/tailwind_renderer.rb
83
168
  - lib/cosensee/tailwind_renderer/blank_bracket.rb
84
169
  - lib/cosensee/tailwind_renderer/code.rb
85
- - lib/cosensee/tailwind_renderer/codeblock_builder.rb
170
+ - lib/cosensee/tailwind_renderer/codeblock.rb
86
171
  - lib/cosensee/tailwind_renderer/command_line.rb
87
172
  - lib/cosensee/tailwind_renderer/decorate_bracket.rb
88
173
  - lib/cosensee/tailwind_renderer/double_bracket.rb
89
174
  - lib/cosensee/tailwind_renderer/empty_bracket.rb
90
175
  - lib/cosensee/tailwind_renderer/external_link_bracket.rb
91
176
  - lib/cosensee/tailwind_renderer/formula_bracket.rb
177
+ - lib/cosensee/tailwind_renderer/gyazo_image_bracket.rb
92
178
  - lib/cosensee/tailwind_renderer/hash_tag.rb
93
179
  - lib/cosensee/tailwind_renderer/icon_bracket.rb
94
180
  - lib/cosensee/tailwind_renderer/image_bracket.rb
181
+ - lib/cosensee/tailwind_renderer/inline_svg_text.rb
95
182
  - lib/cosensee/tailwind_renderer/internal_link_bracket.rb
96
183
  - lib/cosensee/tailwind_renderer/link.rb
97
184
  - lib/cosensee/tailwind_renderer/page.rb
98
185
  - lib/cosensee/tailwind_renderer/parsed_line.rb
99
186
  - lib/cosensee/tailwind_renderer/quote.rb
187
+ - lib/cosensee/tailwind_renderer/spotify_playlist_bracket.rb
100
188
  - lib/cosensee/tailwind_renderer/text_bracket.rb
101
- - lib/cosensee/text_bracket.rb
189
+ - lib/cosensee/tailwind_renderer/youtube_bracket.rb
102
190
  - lib/cosensee/user.rb
103
191
  - lib/cosensee/version.rb
192
+ - lib/cosensee/web_content_generator.rb
193
+ - lib/cosensee/web_server.rb
194
+ - lib/cosensee/web_server/static_file_handler.rb
104
195
  - sig/cosensee.rbs
105
196
  - templates/index.html.erb
106
197
  - templates/page.html.erb
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Cosensee
4
- # for bracket only spaces
5
- BlankBracket = Data.define(:content, :blank, :raw) do
6
- include BracketSerializer
7
- end
8
- end
data/lib/cosensee/code.rb DELETED
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Cosensee
6
- # parse a line
7
- Code = Data.define(:content, :raw) do
8
- alias_method :to_s, :raw
9
-
10
- def to_obj
11
- "`#{content}`"
12
- end
13
-
14
- def to_json(*)
15
- to_obj.to_json(*)
16
- end
17
- end
18
- end
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'json'
4
-
5
- module Cosensee
6
- # for codeblock
7
- Codeblock = Data.define(:content, :raw) do
8
- alias_method :to_s, :raw
9
-
10
- def to_obj
11
- "code:#{content}"
12
- end
13
-
14
- def to_json(*)
15
- to_obj.to_json(*)
16
- end
17
- end
18
- end