notion_to_md 2.5.1 → 3.0.0.beta2
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 +4 -4
- data/README.md +94 -83
- data/lib/notion_to_md/blocks/block.rb +45 -16
- data/lib/notion_to_md/blocks/factory.rb +49 -17
- data/lib/notion_to_md/blocks/normalizer.rb +82 -14
- data/lib/notion_to_md/blocks/numbered_list_item_block.rb +1 -3
- data/lib/notion_to_md/blocks/{types.rb → renderer.rb} +17 -1
- data/lib/notion_to_md/blocks/to_do_list_item_block.rb +0 -2
- data/lib/notion_to_md/database/builder.rb +107 -0
- data/lib/notion_to_md/database.rb +114 -0
- data/lib/notion_to_md/logger.rb +1 -0
- data/lib/notion_to_md/metadata_type.rb +170 -0
- data/lib/notion_to_md/page/builder.rb +112 -0
- data/lib/notion_to_md/page.rb +80 -106
- data/lib/notion_to_md/support/frontmatter.rb +119 -0
- data/lib/notion_to_md/support/metadata_properties.rb +102 -0
- data/lib/notion_to_md/support/pagination.rb +55 -0
- data/lib/notion_to_md/support/yaml_sanitizer.rb +42 -0
- data/lib/notion_to_md/text.rb +27 -0
- data/lib/notion_to_md/text_annotation.rb +41 -9
- data/lib/notion_to_md/version.rb +1 -1
- data/lib/notion_to_md.rb +65 -38
- metadata +24 -103
- data/lib/notion_to_md/blocks/builder.rb +0 -67
- data/lib/notion_to_md/blocks.rb +0 -35
- data/lib/notion_to_md/converter.rb +0 -72
- data/lib/notion_to_md/helpers/yaml_sanitizer.rb +0 -19
- data/lib/notion_to_md/helpers.rb +0 -3
- data/lib/notion_to_md/page_property.rb +0 -106
data/lib/notion_to_md/page.rb
CHANGED
@@ -3,135 +3,109 @@
|
|
3
3
|
class NotionToMd
|
4
4
|
# === NotionToMd::Page
|
5
5
|
#
|
6
|
-
#
|
6
|
+
# Represents a Notion page and allows conversion to Markdown.
|
7
|
+
#
|
8
|
+
# A `Page` object encapsulates the page metadata, its content blocks,
|
9
|
+
# and optionally the frontmatter section.
|
10
|
+
#
|
11
|
+
# @example Convert a Notion page to Markdown
|
12
|
+
# notion_client = Notion::Client.new(token: ENV["NOTION_TOKEN"])
|
13
|
+
# page = NotionToMd::Page.call(id: "xxxx-xxxx", notion_client: notion_client, frontmatter: true)
|
14
|
+
# File.write("page.md", page.to_s)
|
15
|
+
#
|
7
16
|
class Page
|
8
|
-
include
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
include Support::MetadataProperties
|
18
|
+
include Support::Frontmatter
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Build a new {Page} from the Notion API.
|
22
|
+
#
|
23
|
+
# @param id [String] The Notion page ID.
|
24
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
25
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in the output.
|
26
|
+
#
|
27
|
+
# @return [NotionToMd::Page] a new page instance.
|
28
|
+
#
|
29
|
+
# @see .build
|
30
|
+
def call(id:, notion_client:, frontmatter: false)
|
31
|
+
new(id: id, notion_client: notion_client, frontmatter: frontmatter).call
|
21
32
|
end
|
22
|
-
end
|
23
33
|
|
24
|
-
|
25
|
-
|
34
|
+
# @!method build(...)
|
35
|
+
# Alias of {.call}.
|
36
|
+
alias build call
|
26
37
|
end
|
27
38
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
39
|
+
# @return [String] The Notion id page.
|
40
|
+
attr_reader :id
|
31
41
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def created_time
|
37
|
-
page['created_time']
|
38
|
-
end
|
42
|
+
# @param jNotion::Client] The Notion API client.
|
43
|
+
attr_reader :notion_client
|
39
44
|
|
40
|
-
|
41
|
-
|
42
|
-
end
|
45
|
+
# @param [Hash] The page configuration options.
|
46
|
+
attr_reader :config
|
43
47
|
|
44
|
-
|
45
|
-
|
46
|
-
end
|
48
|
+
# @return [Object] The metadata associated with the page.
|
49
|
+
attr_reader :metadata
|
47
50
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
51
|
+
# @return [Array<#to_md>] The list of child blocks belonging to the page.
|
52
|
+
attr_reader :children
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
54
|
+
# @!method blocks
|
55
|
+
# Alias for {#children}.
|
56
|
+
alias blocks children
|
55
57
|
|
56
|
-
|
57
|
-
|
58
|
+
# Initialize a new Page.
|
59
|
+
#
|
60
|
+
# @param id [String] The Notion page ID.
|
61
|
+
# @param notion_client [Notion::Client] The Notion API client.
|
62
|
+
# @param frontmatter [Boolean] Whether to include frontmatter in the Markdown output.
|
63
|
+
def initialize(id:, notion_client:, frontmatter: false)
|
64
|
+
@id = id
|
65
|
+
@notion_client = notion_client
|
66
|
+
@config = { frontmatter: frontmatter }
|
58
67
|
end
|
59
68
|
|
60
|
-
|
61
|
-
|
69
|
+
# Fetch page data and child blocks from the Notion API.
|
70
|
+
#
|
71
|
+
# This method populates the page's metadata and children by making API calls
|
72
|
+
# to retrieve the page content and its nested blocks.
|
73
|
+
#
|
74
|
+
# @return [NotionToMd::Page] returns self to allow method chaining.
|
75
|
+
def call
|
76
|
+
@metadata = notion_client.page(page_id: id)
|
77
|
+
@children = Builder.call(block_id: id, notion_client: notion_client)
|
78
|
+
self
|
62
79
|
end
|
63
80
|
|
64
|
-
|
65
|
-
page[:archived]
|
66
|
-
end
|
81
|
+
alias build call
|
67
82
|
|
83
|
+
# Render the body of the page (Markdown representation of its blocks).
|
84
|
+
#
|
85
|
+
# @return [String] The Markdown content of the page.
|
68
86
|
def body
|
69
87
|
@body ||= blocks.map(&:to_md).compact.join
|
70
88
|
end
|
71
89
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end.join("\n")}
|
78
|
-
---
|
79
|
-
CONTENT
|
90
|
+
# Whether the page includes frontmatter.
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
def frontmatter?
|
94
|
+
config[:frontmatter]
|
80
95
|
end
|
81
96
|
|
82
|
-
|
83
|
-
|
97
|
+
# Render the page as a Markdown string, including optional frontmatter.
|
98
|
+
#
|
99
|
+
# @return [String] The Markdown document.
|
100
|
+
def to_s
|
101
|
+
<<~MD
|
102
|
+
#{frontmatter if frontmatter?}
|
103
|
+
#{body}
|
104
|
+
MD
|
84
105
|
end
|
85
106
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
# rubocop:disable Metrics/MethodLength
|
91
|
-
def default_props
|
92
|
-
@default_props ||= {
|
93
|
-
'id' => id,
|
94
|
-
'title' => escape_frontmatter_value(title),
|
95
|
-
'created_time' => created_time,
|
96
|
-
'cover' => cover,
|
97
|
-
'icon' => icon,
|
98
|
-
'last_edited_time' => last_edited_time,
|
99
|
-
'archived' => archived,
|
100
|
-
'created_by_object' => created_by_object,
|
101
|
-
'created_by_id' => created_by_id,
|
102
|
-
'last_edited_by_object' => last_edited_by_object,
|
103
|
-
'last_edited_by_id' => last_edited_by_id
|
104
|
-
}
|
105
|
-
end
|
106
|
-
# rubocop:enable Metrics/MethodLength
|
107
|
-
|
108
|
-
# This class is kept for retro compatibility reasons.
|
109
|
-
# Use instead the PageProperty class.
|
110
|
-
class CustomProperty < PageProperty
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
def filtered_custom_properties
|
116
|
-
build_custom_properties.reject { |_k, v| v.presence.nil? }
|
117
|
-
end
|
118
|
-
|
119
|
-
def build_custom_properties
|
120
|
-
page.properties.each_with_object({}) do |(name, value), memo|
|
121
|
-
type = value.type
|
122
|
-
next unless valid_custom_property_type?(type)
|
123
|
-
|
124
|
-
key = name.parameterize.underscore
|
125
|
-
memo[key] = build_custom_property(type, value)
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
def valid_custom_property_type?(type)
|
130
|
-
CustomProperty.respond_to?(type.to_sym)
|
131
|
-
end
|
132
|
-
|
133
|
-
def build_custom_property(type, value)
|
134
|
-
CustomProperty.send(type, value)
|
135
|
-
end
|
107
|
+
# @!method to_md
|
108
|
+
# Alias for {#to_s}.
|
109
|
+
alias to_md to_s
|
136
110
|
end
|
137
111
|
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
module Support
|
5
|
+
# === NotionToMd::Support::Frontmatter
|
6
|
+
#
|
7
|
+
# Mixin module responsible for generating YAML frontmatter for
|
8
|
+
# Notion pages and databases. Combines default metadata (id, title,
|
9
|
+
# timestamps, cover, icon, etc.) with supported custom properties.
|
10
|
+
#
|
11
|
+
# @example Use in a page class
|
12
|
+
# class Page
|
13
|
+
# include NotionToMd::Support::MetadataProperties
|
14
|
+
# include NotionToMd::Support::Frontmatter
|
15
|
+
# attr_reader :metadata
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# page = Page.new(metadata: notion_page_hash)
|
19
|
+
# puts page.frontmatter
|
20
|
+
# # =>
|
21
|
+
# # ---
|
22
|
+
# # id: 1234
|
23
|
+
# # title: "Hello World"
|
24
|
+
# # created_time: 2025-01-01 00:00:00 +0000
|
25
|
+
# # ...
|
26
|
+
# # ---
|
27
|
+
#
|
28
|
+
# @see NotionToMd::Support::YamlSanitizer
|
29
|
+
# @see NotionToMd::MetadataType
|
30
|
+
module Frontmatter
|
31
|
+
include YamlSanitizer
|
32
|
+
|
33
|
+
# Generate the YAML frontmatter string by merging default and custom properties.
|
34
|
+
#
|
35
|
+
# @return [String] YAML frontmatter block, surrounded by `---` markers.
|
36
|
+
def frontmatter
|
37
|
+
@frontmatter ||= <<~CONTENT
|
38
|
+
---
|
39
|
+
#{frontmatter_properties.to_a.map do |k, v|
|
40
|
+
"#{k}: #{v}"
|
41
|
+
end.join("\n")}
|
42
|
+
---
|
43
|
+
CONTENT
|
44
|
+
end
|
45
|
+
|
46
|
+
# Merge custom and default properties into the final set of frontmatter properties.
|
47
|
+
#
|
48
|
+
# @return [Hash{String => Object}]
|
49
|
+
def frontmatter_properties
|
50
|
+
@frontmatter_properties ||= frontmatter_custom_properties.deep_merge(frontmatter_default_properties)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Retrieve sanitized custom properties.
|
56
|
+
#
|
57
|
+
# @return [Hash{String => Object}]
|
58
|
+
def frontmatter_custom_properties
|
59
|
+
@frontmatter_custom_properties ||= compact_frontmatter_custom_properties
|
60
|
+
end
|
61
|
+
|
62
|
+
# Remove nil/blank values from custom properties.
|
63
|
+
#
|
64
|
+
# @return [Hash{String => Object}]
|
65
|
+
def compact_frontmatter_custom_properties
|
66
|
+
build_frontmatter_custom_properties.reject { |_k, v| v.presence.nil? }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Build supported custom properties based on their type.
|
70
|
+
#
|
71
|
+
# @return [Hash{String => Object}]
|
72
|
+
def build_frontmatter_custom_properties
|
73
|
+
properties.each_with_object({}) do |(name, value), memo|
|
74
|
+
type = value.type
|
75
|
+
next unless valid_custom_property_type?(type)
|
76
|
+
|
77
|
+
key = name.parameterize.underscore
|
78
|
+
memo[key] = build_custom_property(type, value)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Determine whether a custom property type is supported.
|
83
|
+
#
|
84
|
+
# @param type [String, Symbol]
|
85
|
+
# @return [Boolean]
|
86
|
+
def valid_custom_property_type?(type)
|
87
|
+
MetadataType.respond_to?(type.to_sym)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Convert a Notion property into a YAML-safe value.
|
91
|
+
#
|
92
|
+
# @param type [String, Symbol] The property type.
|
93
|
+
# @param value [Object] The raw Notion property value.
|
94
|
+
# @return [Object] The normalized value suitable for YAML.
|
95
|
+
def build_custom_property(type, value)
|
96
|
+
MetadataType.send(type, value)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Default frontmatter properties derived from metadata.
|
100
|
+
#
|
101
|
+
# @return [Hash{String => Object}]
|
102
|
+
def frontmatter_default_properties
|
103
|
+
@frontmatter_default_properties ||= {
|
104
|
+
'id' => metadata['id'],
|
105
|
+
'title' => escape_frontmatter_value(title),
|
106
|
+
'created_time' => created_time,
|
107
|
+
'cover' => cover,
|
108
|
+
'icon' => icon,
|
109
|
+
'last_edited_time' => last_edited_time,
|
110
|
+
'archived' => archived,
|
111
|
+
'created_by_object' => created_by_object,
|
112
|
+
'created_by_id' => created_by_id,
|
113
|
+
'last_edited_by_object' => last_edited_by_object,
|
114
|
+
'last_edited_by_id' => last_edited_by_id
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
module Support
|
5
|
+
# === NotionToMd::Support::MetadataProperties
|
6
|
+
#
|
7
|
+
# Mixin module providing convenience accessors for Notion page or
|
8
|
+
# database metadata. It extracts common properties (id, title, timestamps,
|
9
|
+
# cover, icon, etc.) from the `metadata` hash returned by the Notion API.
|
10
|
+
#
|
11
|
+
# When included, this module delegates `#properties` to the `metadata`
|
12
|
+
# attribute of the including class.
|
13
|
+
#
|
14
|
+
# @example Include in a model
|
15
|
+
# class Page
|
16
|
+
# include NotionToMd::Support::MetadataProperties
|
17
|
+
# attr_reader :metadata
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# page = Page.new(metadata: notion_page_hash)
|
21
|
+
# page.title # => "My Notion Page"
|
22
|
+
#
|
23
|
+
# @see NotionToMd::MetadataType
|
24
|
+
module MetadataProperties
|
25
|
+
def self.included(base)
|
26
|
+
base.extend Forwardable
|
27
|
+
base.def_delegators :metadata, :properties
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extract the page or database title.
|
31
|
+
#
|
32
|
+
# @return [String] Plain text concatenated from `title` property.
|
33
|
+
def title
|
34
|
+
title_list =
|
35
|
+
metadata[:title] ||
|
36
|
+
metadata.dig(:properties, :Name, :title) ||
|
37
|
+
metadata.dig(:properties, :title, :title)
|
38
|
+
|
39
|
+
title_list.inject('') do |acc, slug|
|
40
|
+
acc + slug[:plain_text]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Time] Creation timestamp.
|
45
|
+
def created_time
|
46
|
+
metadata[:created_time]
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [String, nil] Object type of creator.
|
50
|
+
def created_by_object
|
51
|
+
metadata.dig(:created_by, :object)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [String, nil] ID of creator.
|
55
|
+
def created_by_id
|
56
|
+
metadata.dig(:created_by, :id)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Time] Last edited timestamp.
|
60
|
+
def last_edited_time
|
61
|
+
metadata[:last_edited_time]
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String, nil] Object type of last editor.
|
65
|
+
def last_edited_by_object
|
66
|
+
metadata.dig(:last_edited_by, :object)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String, nil] ID of last editor.
|
70
|
+
def last_edited_by_id
|
71
|
+
metadata.dig(:last_edited_by, :id)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [String] Public URL of the record.
|
75
|
+
def url
|
76
|
+
metadata[:url]
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Boolean] Whether the record is archived.
|
80
|
+
def archived
|
81
|
+
metadata[:archived]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Extract the cover image URL, from either external or file source.
|
85
|
+
#
|
86
|
+
# @return [String, nil] Cover URL or nil if not present.
|
87
|
+
def cover
|
88
|
+
MetadataType.external(metadata[:cover]) ||
|
89
|
+
MetadataType.file(metadata[:cover])
|
90
|
+
end
|
91
|
+
|
92
|
+
# Extract the icon (emoji, external, or file).
|
93
|
+
#
|
94
|
+
# @return [String, nil] Icon value or nil if not present.
|
95
|
+
def icon
|
96
|
+
MetadataType.emoji(metadata[:icon]) ||
|
97
|
+
MetadataType.external(metadata[:icon]) ||
|
98
|
+
MetadataType.file(metadata[:icon])
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
module Support
|
5
|
+
##
|
6
|
+
# The Pagination module provides a helper for iterating through paginated
|
7
|
+
# Notion API responses.
|
8
|
+
#
|
9
|
+
# Many Notion API endpoints (e.g., `block_children`, `database_query`) return
|
10
|
+
# a limited set of results and include `has_more` and `next_cursor` fields.
|
11
|
+
# This module encapsulates the common logic to repeatedly fetch all pages
|
12
|
+
# until no more results are available.
|
13
|
+
#
|
14
|
+
# @example Paginate through all blocks of a page
|
15
|
+
# include NotionToMd::Support::Pagination
|
16
|
+
#
|
17
|
+
# all_blocks = paginate do |cursor|
|
18
|
+
# notion_client.block_children(
|
19
|
+
# block_id: "xxxx-xxxx",
|
20
|
+
# start_cursor: cursor
|
21
|
+
# )
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# all_blocks.each { |block| puts block.type }
|
25
|
+
#
|
26
|
+
module Pagination
|
27
|
+
##
|
28
|
+
# Iterates through all pages of a paginated Notion API response.
|
29
|
+
#
|
30
|
+
# The given block is called with the current cursor (or `nil` for the
|
31
|
+
# first call). It must return a response object that responds to
|
32
|
+
# `results`, `has_more`, and `next_cursor`.
|
33
|
+
#
|
34
|
+
# @yieldparam [String, nil] cursor the current cursor to start fetching from
|
35
|
+
# @yieldreturn [Object] a response object containing `results`, `has_more`, and `next_cursor`
|
36
|
+
# @return [Array] a flat array of all accumulated results from each page
|
37
|
+
def paginate
|
38
|
+
results = []
|
39
|
+
cursor = nil
|
40
|
+
|
41
|
+
loop do
|
42
|
+
resp = yield(cursor)
|
43
|
+
|
44
|
+
results.concat(resp.results)
|
45
|
+
|
46
|
+
break unless resp.has_more && resp.next_cursor
|
47
|
+
|
48
|
+
cursor = resp.next_cursor
|
49
|
+
end
|
50
|
+
|
51
|
+
results
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class NotionToMd
|
4
|
+
module Support
|
5
|
+
# === NotionToMd::Support::YamlSanitizer
|
6
|
+
#
|
7
|
+
# Provides helpers to sanitize values before inserting them into
|
8
|
+
# YAML frontmatter. This prevents syntax errors when values contain
|
9
|
+
# characters that YAML treats specially (e.g. colons, dashes).
|
10
|
+
#
|
11
|
+
# @example Escape a simple value
|
12
|
+
# include NotionToMd::Support::YamlSanitizer
|
13
|
+
#
|
14
|
+
# escape_frontmatter_value("Hello World")
|
15
|
+
# # => "Hello World"
|
16
|
+
#
|
17
|
+
# @example Escape a value containing a colon
|
18
|
+
# escape_frontmatter_value("Title: Subtitle")
|
19
|
+
# # => "\"Title: Subtitle\""
|
20
|
+
#
|
21
|
+
# @example Escape a value containing dash + space
|
22
|
+
# escape_frontmatter_value("- item")
|
23
|
+
# # => "\"- item\""
|
24
|
+
#
|
25
|
+
module YamlSanitizer
|
26
|
+
# Escape a frontmatter value if it contains a colon (`:`) followed by
|
27
|
+
# a space, or a dash followed by a space (`- `). Wraps the string in
|
28
|
+
# double quotes and escapes any embedded quotes.
|
29
|
+
#
|
30
|
+
# @param value [String] The raw value to escape.
|
31
|
+
# @return [String] A safe string suitable for inclusion in YAML frontmatter.
|
32
|
+
def escape_frontmatter_value(value)
|
33
|
+
if value.match?(/: |-\s/)
|
34
|
+
# Escape embedded double quotes to keep valid YAML
|
35
|
+
"\"#{value.gsub('"', '\"')}\""
|
36
|
+
else
|
37
|
+
value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/notion_to_md/text.rb
CHANGED
@@ -1,12 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class NotionToMd
|
4
|
+
# === NotionToMd::Text
|
5
|
+
#
|
6
|
+
# Utility class to render inline Notion text elements into Markdown.
|
7
|
+
# Used by {NotionToMd::Blocks::Renderer} when processing `rich_text` arrays.
|
8
|
+
#
|
9
|
+
# Supported types:
|
10
|
+
# * `text` → plain text
|
11
|
+
# * `equation` → LaTeX inline math, wrapped as `$`…`$`
|
12
|
+
#
|
13
|
+
# @example Render plain text
|
14
|
+
# NotionToMd::Text.text({ plain_text: "Hello" })
|
15
|
+
# # => "Hello"
|
16
|
+
#
|
17
|
+
# @example Render an inline equation
|
18
|
+
# NotionToMd::Text.equation({ plain_text: "E=mc^2" })
|
19
|
+
# # => "$`E=mc^2`$"
|
20
|
+
#
|
21
|
+
# @see NotionToMd::Blocks::Renderer
|
22
|
+
# @see NotionToMd::TextAnnotation
|
4
23
|
class Text
|
5
24
|
class << self
|
25
|
+
# Render a plain text element.
|
26
|
+
#
|
27
|
+
# @param text [Hash] A Notion text object with key `:plain_text`.
|
28
|
+
# @return [String] The raw text.
|
6
29
|
def text(text)
|
7
30
|
text[:plain_text]
|
8
31
|
end
|
9
32
|
|
33
|
+
# Render an inline equation element.
|
34
|
+
#
|
35
|
+
# @param text [Hash] A Notion text object with key `:plain_text`.
|
36
|
+
# @return [String] Inline math expression wrapped with `$...$`.
|
10
37
|
def equation(text)
|
11
38
|
"$`#{text[:plain_text]}`$"
|
12
39
|
end
|
@@ -1,37 +1,69 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class NotionToMd
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
# *
|
11
|
-
# *
|
12
|
-
|
4
|
+
# === NotionToMd::TextAnnotation
|
5
|
+
#
|
6
|
+
# Utility class to wrap text with Markdown (or HTML) syntax
|
7
|
+
# corresponding to Notion's text annotations.
|
8
|
+
#
|
9
|
+
# Supported annotations:
|
10
|
+
# * `italic` → `*text*`
|
11
|
+
# * `bold` → `**text**`
|
12
|
+
# * `strikethrough` → `~~text~~`
|
13
|
+
# * `underline` → `<u>text</u>` (HTML, since Markdown does not support underline)
|
14
|
+
# * `code` → `` `text` ``
|
15
|
+
# * `color` → (not supported, returns text as-is)
|
16
|
+
#
|
17
|
+
# @example Apply bold and italic
|
18
|
+
# NotionToMd::TextAnnotation.bold("Hello") # => "**Hello**"
|
19
|
+
# NotionToMd::TextAnnotation.italic("World") # => "*World*"
|
20
|
+
#
|
21
|
+
# @example Apply underline
|
22
|
+
# NotionToMd::TextAnnotation.underline("Note") # => "<u>Note</u>"
|
23
|
+
#
|
24
|
+
# @see NotionToMd::Blocks::Renderer
|
13
25
|
class TextAnnotation
|
14
26
|
class << self
|
27
|
+
# Apply italic annotation.
|
28
|
+
# @param text [String]
|
29
|
+
# @return [String]
|
15
30
|
def italic(text)
|
16
31
|
"*#{text}*"
|
17
32
|
end
|
18
33
|
|
34
|
+
# Apply bold annotation.
|
35
|
+
# @param text [String]
|
36
|
+
# @return [String]
|
19
37
|
def bold(text)
|
20
38
|
"**#{text}**"
|
21
39
|
end
|
22
40
|
|
41
|
+
# Apply strikethrough annotation.
|
42
|
+
# @param text [String]
|
43
|
+
# @return [String]
|
23
44
|
def strikethrough(text)
|
24
45
|
"~~#{text}~~"
|
25
46
|
end
|
26
47
|
|
48
|
+
# Apply underline annotation (HTML).
|
49
|
+
# @param text [String]
|
50
|
+
# @return [String]
|
27
51
|
def underline(text)
|
28
52
|
"<u>#{text}</u>"
|
29
53
|
end
|
30
54
|
|
55
|
+
# Apply inline code annotation.
|
56
|
+
# @param text [String]
|
57
|
+
# @return [String]
|
31
58
|
def code(text)
|
32
59
|
"`#{text}`"
|
33
60
|
end
|
34
61
|
|
62
|
+
# Color annotation is not supported in Markdown.
|
63
|
+
# Currently returns text unchanged.
|
64
|
+
#
|
65
|
+
# @param text [String]
|
66
|
+
# @return [String]
|
35
67
|
def color(text)
|
36
68
|
text
|
37
69
|
end
|
data/lib/notion_to_md/version.rb
CHANGED