cosensee 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +56 -0
- data/.rubocop_todo.yml +7 -0
- data/README.md +40 -0
- data/Rakefile +10 -0
- data/lib/cosensee/blank_bracket.rb +8 -0
- data/lib/cosensee/bracket_parser.rb +82 -0
- data/lib/cosensee/bracket_serializer.rb +26 -0
- data/lib/cosensee/code.rb +18 -0
- data/lib/cosensee/codeblock.rb +18 -0
- data/lib/cosensee/command_line.rb +18 -0
- data/lib/cosensee/decorate_bracket.rb +16 -0
- data/lib/cosensee/double_bracket.rb +20 -0
- data/lib/cosensee/empty_bracket.rb +8 -0
- data/lib/cosensee/error.rb +5 -0
- data/lib/cosensee/external_link_bracket.rb +8 -0
- data/lib/cosensee/formula_bracket.rb +8 -0
- data/lib/cosensee/hash_tag.rb +19 -0
- data/lib/cosensee/html_builder.rb +59 -0
- data/lib/cosensee/html_encodable.rb +12 -0
- data/lib/cosensee/icon_bracket.rb +8 -0
- data/lib/cosensee/image_bracket.rb +8 -0
- data/lib/cosensee/indent.rb +25 -0
- data/lib/cosensee/internal_link_bracket.rb +8 -0
- data/lib/cosensee/line.rb +47 -0
- data/lib/cosensee/line_parser.rb +253 -0
- data/lib/cosensee/link.rb +18 -0
- data/lib/cosensee/link_encodable.rb +26 -0
- data/lib/cosensee/page.rb +68 -0
- data/lib/cosensee/page_store.rb +63 -0
- data/lib/cosensee/parsed_bracket.rb +42 -0
- data/lib/cosensee/parsed_line.rb +76 -0
- data/lib/cosensee/project.rb +46 -0
- data/lib/cosensee/quote.rb +22 -0
- data/lib/cosensee/tailwind_renderer/blank_bracket.rb +11 -0
- data/lib/cosensee/tailwind_renderer/code.rb +14 -0
- data/lib/cosensee/tailwind_renderer/codeblock_builder.rb +37 -0
- data/lib/cosensee/tailwind_renderer/command_line.rb +17 -0
- data/lib/cosensee/tailwind_renderer/decorate_bracket.rb +25 -0
- data/lib/cosensee/tailwind_renderer/double_bracket.rb +13 -0
- data/lib/cosensee/tailwind_renderer/empty_bracket.rb +11 -0
- data/lib/cosensee/tailwind_renderer/external_link_bracket.rb +15 -0
- data/lib/cosensee/tailwind_renderer/formula_bracket.rb +13 -0
- data/lib/cosensee/tailwind_renderer/hash_tag.rb +17 -0
- data/lib/cosensee/tailwind_renderer/icon_bracket.rb +14 -0
- data/lib/cosensee/tailwind_renderer/image_bracket.rb +15 -0
- data/lib/cosensee/tailwind_renderer/internal_link_bracket.rb +15 -0
- data/lib/cosensee/tailwind_renderer/link.rb +14 -0
- data/lib/cosensee/tailwind_renderer/page.rb +31 -0
- data/lib/cosensee/tailwind_renderer/parsed_line.rb +30 -0
- data/lib/cosensee/tailwind_renderer/quote.rb +14 -0
- data/lib/cosensee/tailwind_renderer/text_bracket.rb +12 -0
- data/lib/cosensee/tailwind_renderer.rb +36 -0
- data/lib/cosensee/text_bracket.rb +10 -0
- data/lib/cosensee/user.rb +37 -0
- data/lib/cosensee/version.rb +5 -0
- data/lib/cosensee.rb +54 -0
- data/sig/cosensee.rbs +4 -0
- data/templates/index.html.erb +39 -0
- data/templates/page.html.erb +59 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ca076a9a5e18e0bd0f5c1825e35714536bcfad2921b8d3e997a3fe86140314b4
|
4
|
+
data.tar.gz: e28ec42e8750b9426275e78ba6b238b358217d8c97c7d95c1134a5f54f6f341c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 72a7eb0c65b77920c3b1784db4bf58f9d984c0f660dbda56cab55ac2ac91d83ecf3e47b08ffb413d08e1601180f1258adcb141470a1dcbe5b653bd8d77232d7b
|
7
|
+
data.tar.gz: 1a8bef01b367b5d8f665236e0940fb4cf823134d4cbe8a3955eab932cf76e63d21c9548b813acadfd16c5da57ee17a40cf350ed093e1c23f3955ede40862954f
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: '3.4'
|
5
|
+
NewCops: enable
|
6
|
+
|
7
|
+
require:
|
8
|
+
- rubocop-rake
|
9
|
+
- rubocop-rspec
|
10
|
+
|
11
|
+
Layout/LineLength:
|
12
|
+
Max: 300
|
13
|
+
Exclude:
|
14
|
+
- 'spec/**/*'
|
15
|
+
|
16
|
+
Layout/MultilineMethodCallIndentation:
|
17
|
+
EnforcedStyle: indented_relative_to_receiver
|
18
|
+
|
19
|
+
Metrics/AbcSize:
|
20
|
+
Max: 60
|
21
|
+
|
22
|
+
Metrics/ClassLength:
|
23
|
+
Max: 300
|
24
|
+
|
25
|
+
Metrics/CyclomaticComplexity:
|
26
|
+
Max: 20
|
27
|
+
|
28
|
+
Metrics/BlockLength:
|
29
|
+
Max: 100
|
30
|
+
|
31
|
+
Metrics/MethodLength:
|
32
|
+
Max: 80
|
33
|
+
|
34
|
+
Metrics/ParameterLists:
|
35
|
+
Max: 8
|
36
|
+
|
37
|
+
Metrics/PerceivedComplexity:
|
38
|
+
Max: 20
|
39
|
+
|
40
|
+
Style/MinMaxComparison:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
Style/NumericLiterals:
|
44
|
+
Enabled: false
|
45
|
+
|
46
|
+
RSpec/DescribedClass:
|
47
|
+
Enabled: false
|
48
|
+
|
49
|
+
RSpec/ExampleLength:
|
50
|
+
Max: 50
|
51
|
+
|
52
|
+
RSpec/MultipleExpectations:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
Style/NumericPredicate:
|
56
|
+
Enabled: false
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2024-12-11 09:34:23 UTC using RuboCop version 1.69.1.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Cosensee
|
2
|
+
|
3
|
+
Cosensee is a tool that reads JSON data from [Cosense](https://scrapbox.io) (formerly Scrapbox) and outputs it in HTML format.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Install the gem:
|
8
|
+
|
9
|
+
$ gem install cosensee
|
10
|
+
|
11
|
+
If you use bundler, add to the application's Gemfile by executing:
|
12
|
+
|
13
|
+
$ bundle add cosensee
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
To output HTML, specify the JSON file from Cosense as an argument and run `bin/build` with Cosense JSON dump file.
|
18
|
+
|
19
|
+
$ bin/build <json file>
|
20
|
+
|
21
|
+
The HTML files will be output to the .out directory. You can view them locally by using the bin/server command.
|
22
|
+
|
23
|
+
$ $bin/server
|
24
|
+
|
25
|
+
By adding a Cosense JSON file as an argument, the build process will be executed before starting the server.
|
26
|
+
|
27
|
+
$ $bin/server <json file>
|
28
|
+
|
29
|
+
The default port used by bin/server is 1212.
|
30
|
+
|
31
|
+
## Development
|
32
|
+
|
33
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
34
|
+
|
35
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
36
|
+
|
37
|
+
## Contributing
|
38
|
+
|
39
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/takahashim/cosensee.
|
40
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cosensee
|
4
|
+
# parser of Bracket
|
5
|
+
class BracketParser
|
6
|
+
include ::Cosensee::LinkEncodable
|
7
|
+
|
8
|
+
def self.parse(content)
|
9
|
+
new.parse(content)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :content
|
13
|
+
|
14
|
+
def parse(content)
|
15
|
+
@content = content
|
16
|
+
|
17
|
+
case content
|
18
|
+
in [/\A\z/]
|
19
|
+
EmptyBracket.new(content:, raw: '[]')
|
20
|
+
in [/\A([ \t]+)\z/]
|
21
|
+
BlankBracket.new(content:, blank: Regexp.last_match(1), raw: "[#{Regexp.last_match(0)}]")
|
22
|
+
in [/\A\$ (.*)\z/]
|
23
|
+
FormulaBracket.new(content:, formula: Regexp.last_match(1), raw: "[#{Regexp.last_match(0)}]")
|
24
|
+
in [/\A(.*).icon\z/]
|
25
|
+
IconBracket.new(content:, icon_name: Regexp.last_match(1))
|
26
|
+
in [%r{\A(https?://[^\s]+)\s+(https?://[^\s\]]*\.(png|jpe?g|gif|svg|webp)(\?[^\s\]]+)?)\z}]
|
27
|
+
ImageBracket.new(content:, link: Regexp.last_match(1), src: Regexp.last_match(2), raw: "[#{Regexp.last_match(0)}]")
|
28
|
+
in [%r{\A(https?://[^\s\]]*\.(png|jpe?g|gif|svg|webp)(\?[^\s\]]+)?)\s+(https?://[^\s]+)\z}]
|
29
|
+
ImageBracket.new(content:, link: Regexp.last_match(4), src: Regexp.last_match(1), raw: "[#{Regexp.last_match(0)}]")
|
30
|
+
in [%r{\A(https?://[^\s\]]*\.(png|jpe?g|gif|svg|webp))\z}]
|
31
|
+
ImageBracket.new(content:, link: nil, src: Regexp.last_match(1), raw: "[#{Regexp.last_match(0)}]")
|
32
|
+
in [%r{\A(https?://[^ \t]*)(\s+(.+))?\z}]
|
33
|
+
# match_external_link_precede
|
34
|
+
anchor = Regexp.last_match(3) || Regexp.last_match(1)
|
35
|
+
link = Regexp.last_match(1)
|
36
|
+
raw = "[#{Regexp.last_match(0)}]"
|
37
|
+
ExternalLinkBracket.new(content:, link:, anchor:, raw:)
|
38
|
+
in [%r{\A((.*\S)\s+)?(https?://[^\s]+)\z}]
|
39
|
+
# match_external_link_succeed
|
40
|
+
anchor = Regexp.last_match(2) || Regexp.last_match(3)
|
41
|
+
link = Regexp.last_match(3)
|
42
|
+
raw = "[#{Regexp.last_match(0)}]"
|
43
|
+
ExternalLinkBracket.new(content:, link:, anchor:, raw:)
|
44
|
+
in [%r{\A([_\*/\-"#%&'\(\)~\|\+<>{},\.]+) (.+)\z}]
|
45
|
+
# match_decorate
|
46
|
+
deco = Regexp.last_match(1)
|
47
|
+
text = Regexp.last_match(2)
|
48
|
+
font_size = deco.count('*') > 10 ? 10 : deco.count('*')
|
49
|
+
underlined = deco.include?('_')
|
50
|
+
deleted = deco.include?('-')
|
51
|
+
slanted = deco.include?('/')
|
52
|
+
DecorateBracket.new(
|
53
|
+
content:,
|
54
|
+
font_size:,
|
55
|
+
underlined:,
|
56
|
+
slanted:,
|
57
|
+
deleted:,
|
58
|
+
text:,
|
59
|
+
raw:
|
60
|
+
)
|
61
|
+
else
|
62
|
+
line_parser = LineParser.new
|
63
|
+
data = ParsedBracket.new(content:)
|
64
|
+
parsed = data
|
65
|
+
.then { |d| line_parser.parse_url(d) }
|
66
|
+
.then { |d| line_parser.parse_hashtag(d) }
|
67
|
+
if parsed.single_text? && parsed.content == content
|
68
|
+
anchor = parsed.first_content
|
69
|
+
link = make_link(anchor)
|
70
|
+
raw = raw_string
|
71
|
+
InternalLinkBracket.new(content:, link:, anchor:, raw:)
|
72
|
+
else
|
73
|
+
TextBracket.new(content: parsed.content, raw: raw_string)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def raw_string
|
79
|
+
"[#{content.map(&:to_s).join}]"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Cosensee
|
6
|
+
# serializer for brackets
|
7
|
+
module BracketSerializer
|
8
|
+
def to_s = raw
|
9
|
+
|
10
|
+
def to_obj
|
11
|
+
unparsed = content.map do |elem|
|
12
|
+
if elem.is_a?(String)
|
13
|
+
elem
|
14
|
+
else
|
15
|
+
elem.to_obj
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
"[#{unparsed}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_json(*)
|
23
|
+
to_obj.to_json(*)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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
|
@@ -0,0 +1,18 @@
|
|
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
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Cosensee
|
6
|
+
# for command line
|
7
|
+
CommandLine = Data.define(:content, :prompt, :raw) do
|
8
|
+
alias_method :to_s, :raw
|
9
|
+
|
10
|
+
def to_obj
|
11
|
+
"#{prompt} #{content}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_json(*)
|
15
|
+
to_obj.to_json(*)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cosensee
|
4
|
+
# for double Bracket
|
5
|
+
DoubleBracket = Data.define(:content, :raw) do
|
6
|
+
def image?
|
7
|
+
content.size == 1 && content[0].match?(/\.(png|jpg)$/)
|
8
|
+
end
|
9
|
+
|
10
|
+
alias_method :to_s, :raw
|
11
|
+
|
12
|
+
def to_obj
|
13
|
+
"[[#{content}]]"
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_json(*)
|
17
|
+
to_obj.to_json(*)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Cosensee
|
6
|
+
# parse a hash tag
|
7
|
+
HashTag = Data.define(:content, :raw) do
|
8
|
+
alias_method :to_s, :raw
|
9
|
+
alias_method :anchor, :content
|
10
|
+
|
11
|
+
def to_obj
|
12
|
+
"##{content}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_json(*)
|
16
|
+
to_obj.to_json(*)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'erubi'
|
5
|
+
require 'tilt'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
module Cosensee
|
9
|
+
# generate HTML files
|
10
|
+
class HtmlBuilder
|
11
|
+
def initialize(project, root_dir: nil)
|
12
|
+
@project = project
|
13
|
+
@templates_dir = File.join(__dir__, '../../templates')
|
14
|
+
@root_dir = root_dir || File.join(Dir.pwd, '.out')
|
15
|
+
FileUtils.mkdir_p(@root_dir)
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :project, :root_dir, :templates_dir
|
19
|
+
|
20
|
+
def build_all
|
21
|
+
build_index(project)
|
22
|
+
|
23
|
+
# build all pages
|
24
|
+
project.pages.each do |page|
|
25
|
+
build_page(page)
|
26
|
+
end
|
27
|
+
|
28
|
+
# build all orphan (title only) pages
|
29
|
+
project.page_store.orphan_page_titles.each do |title|
|
30
|
+
build_page_only_title(title)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_index(project)
|
35
|
+
template = Tilt::ErubiTemplate.new(File.join(templates_dir, 'index.html.erb'))
|
36
|
+
output = template.render(nil, project:)
|
37
|
+
File.write(File.join(root_dir, 'index.html'), output)
|
38
|
+
end
|
39
|
+
|
40
|
+
def build_page(page)
|
41
|
+
template = Tilt::ErubiTemplate.new(File.join(templates_dir, 'page.html.erb'))
|
42
|
+
output = template.render(nil, project:, page:, title: page.title)
|
43
|
+
File.write(File.join(root_dir, page.link_path), output)
|
44
|
+
end
|
45
|
+
|
46
|
+
def build_page_only_title(title)
|
47
|
+
path = File.join(root_dir, "#{title.gsub(/ /, '_').gsub('/', '%2F')}.html")
|
48
|
+
return if File.exist?(path)
|
49
|
+
|
50
|
+
template = Tilt::ErubiTemplate.new(File.join(templates_dir, 'page.html.erb'))
|
51
|
+
output = template.render(nil, project:, page: nil, title:)
|
52
|
+
File.write(path, output)
|
53
|
+
end
|
54
|
+
|
55
|
+
def page_title(page)
|
56
|
+
"#{page.title} | #{project.title}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Cosensee
|
6
|
+
# for normal Text
|
7
|
+
Indent = Data.define(:content, :raw) do
|
8
|
+
def initialize(content: '', raw: '')
|
9
|
+
@level = content.size
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_reader :level
|
14
|
+
|
15
|
+
alias_method :to_s, :raw
|
16
|
+
|
17
|
+
def to_obj
|
18
|
+
content
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_json(*)
|
22
|
+
to_obj.to_json(*)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Cosensee
|
6
|
+
# for Line
|
7
|
+
Line = Data.define(:content, :parsed) do
|
8
|
+
def self.from_array(lines_args)
|
9
|
+
lines_args.map { |arg| new(arg) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(content:)
|
13
|
+
super(
|
14
|
+
content:,
|
15
|
+
parsed: LineParser.parse(content)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
def brackets
|
20
|
+
content.scan(/\[.+?\]/)
|
21
|
+
end
|
22
|
+
|
23
|
+
def some_images?
|
24
|
+
brackets.any? { |item| item.match?(/\.(png|jpg)$/) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def first_image
|
28
|
+
brackets.find { |item| item.match?(/\.(png|jpg)$/) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def indent_level
|
32
|
+
content.match(/\A([\t ]*)/)[1].size
|
33
|
+
end
|
34
|
+
|
35
|
+
def indented?
|
36
|
+
content.match?(/\A[\t ]/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_obj
|
40
|
+
content
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_json(*)
|
44
|
+
to_obj.to_json(*)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|