jekyll-notion-cms 1.0.1 → 1.0.3
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/CHANGELOG.md +23 -0
- data/docs/EXAMPLES_AND_CONFIGURATION.md +40 -15
- data/docs/TASKS.md +26 -0
- data/lib/jekyll_notion_cms/data_organizers.rb +85 -37
- data/lib/jekyll_notion_cms/generator.rb +37 -14
- data/lib/jekyll_notion_cms/version.rb +1 -1
- metadata +3 -4
- data/docs/templates/architecture.excalidraw +0 -3805
- data/docs/templates/architecture.png +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 33cff8a4d2a08b099224d2db5605438b6a49e3a87c83b1d18f3679d19def3a31
|
|
4
|
+
data.tar.gz: 6efe8bd404d78a1816e0f4ed5f0cfa4dbc0f2820d3bc363996ae8e3d82bdd7f9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 24a0b92c12b7f0c2fce898067cbee813d9dbd087697e11a769342f51d8698760aacd2f9d4fc5328641d198e7e5d57f6c4e160a90892eb6cfea760382f8166296
|
|
7
|
+
data.tar.gz: 5c87441340d94046ec3745bc92d20fdb6c4517f0835e7736b45751fc984b616dcfd083f43b887ef0017348511b3432acc08639ec142098a742231b1b7923c14b
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.0.3] - 2026-09-27
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- `items_by_category` adds an item related to several categories to each of them
|
|
13
|
+
- Before, the item was filed under a merged category such as `["Backend", "Frontend"]`
|
|
14
|
+
- Each category takes its own icon, color and order from the rollups
|
|
15
|
+
- Unchanged empty collections are no longer rewritten on every build
|
|
16
|
+
- An empty collection dumps to a single line (`--- []`) that the previous check missed,
|
|
17
|
+
which made `jekyll serve` regenerate in a loop
|
|
18
|
+
- The fallback keeps data files imported from Notion instead of overwriting them
|
|
19
|
+
- Without `NOTION_TOKEN`, or when a database cannot be fetched, the last Notion data stays in `_data/`
|
|
20
|
+
- Files written by the fallback now say so in their header, and are still regenerated
|
|
21
|
+
- Delete a data file to rebuild it from the Jekyll collection
|
|
22
|
+
|
|
23
|
+
## [1.0.2] - 2026-01-26
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Category order now correctly handles array values from rollup properties
|
|
28
|
+
- Fixes sorting of categories when `Category Order` returns an array instead of a single value
|
|
29
|
+
|
|
8
30
|
## [1.0.1] - 2026-01-22
|
|
9
31
|
|
|
10
32
|
### Fixed
|
|
@@ -42,5 +64,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
42
64
|
|
|
43
65
|
- Secure handling of Notion API tokens via environment variables
|
|
44
66
|
|
|
67
|
+
[1.0.2]: https://github.com/maxime-lenne/jekyll-notion-cms/releases/tag/v1.0.2
|
|
45
68
|
[1.0.1]: https://github.com/maxime-lenne/jekyll-notion-cms/releases/tag/v1.0.1
|
|
46
69
|
[1.0.0]: https://github.com/maxime-lenne/jekyll-notion-cms/releases/tag/v1.0.0
|
|
@@ -214,15 +214,19 @@ notion:
|
|
|
214
214
|
|
|
215
215
|
Showcase your technical expertise.
|
|
216
216
|
|
|
217
|
-
**Notion Database Structure
|
|
217
|
+
**Notion Database Structure** (`items_by_category` reads these exact property names — see
|
|
218
|
+
[`items_by_category`](#organizer-types) for details):
|
|
218
219
|
|
|
219
220
|
| Property | Type | Description |
|
|
220
221
|
|----------|------|-------------|
|
|
221
222
|
| Name | Title | Skill name |
|
|
222
|
-
| Category |
|
|
223
|
-
|
|
|
224
|
-
|
|
|
223
|
+
| Category | Rollup | Category name, rolled up from a related Categories database |
|
|
224
|
+
| Icon | Rollup | Category icon, rolled up from Categories |
|
|
225
|
+
| Color | Rollup | Skill color |
|
|
226
|
+
| Category Order | Rollup | Category display order, rolled up from Categories |
|
|
227
|
+
| Level | Number | Proficiency level |
|
|
225
228
|
| Years | Number | Years of experience |
|
|
229
|
+
| Featured | Checkbox | Highlight this skill |
|
|
226
230
|
| Order | Number | Display order within category |
|
|
227
231
|
|
|
228
232
|
**Configuration:**
|
|
@@ -234,15 +238,6 @@ notion:
|
|
|
234
238
|
database_env: NOTION_SKILLS_DB
|
|
235
239
|
data_file: notion_skills.yml
|
|
236
240
|
organizer: items_by_category
|
|
237
|
-
properties:
|
|
238
|
-
- { name: Name, type: title }
|
|
239
|
-
- { name: Category, type: rollup }
|
|
240
|
-
- { name: Level, type: number }
|
|
241
|
-
- { name: Icon, type: rich_text }
|
|
242
|
-
- { name: Years, type: number }
|
|
243
|
-
- { name: Order, type: number }
|
|
244
|
-
- { name: Category Icon, type: rollup, key: category_icon }
|
|
245
|
-
- { name: Category Order, type: rollup, key: category_order }
|
|
246
241
|
```
|
|
247
242
|
|
|
248
243
|
**Template Usage:**
|
|
@@ -256,8 +251,11 @@ notion:
|
|
|
256
251
|
</h3>
|
|
257
252
|
<div class="skills-grid">
|
|
258
253
|
{% for item in category[1].items %}
|
|
259
|
-
<div class="skill">
|
|
260
|
-
<span class="skill-name">
|
|
254
|
+
<div class="skill" style="--skill-color: {{ item.color }}">
|
|
255
|
+
<span class="skill-name">
|
|
256
|
+
{{ item.name }}
|
|
257
|
+
{% if item.featured %}<span class="badge">★</span>{% endif %}
|
|
258
|
+
</span>
|
|
261
259
|
<div class="skill-bar">
|
|
262
260
|
<div class="skill-level" style="width: {{ item.level }}%"></div>
|
|
263
261
|
</div>
|
|
@@ -305,16 +303,43 @@ Groups items by their category. Useful for skills, products, team members, or an
|
|
|
305
303
|
organizer: items_by_category
|
|
306
304
|
```
|
|
307
305
|
|
|
306
|
+
> **Note:** unlike the other organizers, `items_by_category` does **not** use the `properties:`
|
|
307
|
+
> list — it reads a fixed set of property names directly from each page. Your database must use
|
|
308
|
+
> these exact names:
|
|
309
|
+
>
|
|
310
|
+
> | Property | Type | Used for |
|
|
311
|
+
> |----------|------|----------|
|
|
312
|
+
> | `Name` | Title | Item name |
|
|
313
|
+
> | `Category` | Rollup | Category name (groups items together) |
|
|
314
|
+
> | `Icon` | Rollup | Category icon |
|
|
315
|
+
> | `Color` | Rollup | Item color |
|
|
316
|
+
> | `Category Order` | Rollup | Category display order |
|
|
317
|
+
> | `Level` | Number | Item level |
|
|
318
|
+
> | `Years` | Number | Item years |
|
|
319
|
+
> | `Featured` | Checkbox | Item featured flag |
|
|
320
|
+
> | `Order` | Number | Item display order within its category |
|
|
321
|
+
>
|
|
322
|
+
> `Category`, `Icon` and `Category Order` are typically rollups from a *Categories* database via
|
|
323
|
+
> a relation field, so all items in the same category share the same icon/order.
|
|
324
|
+
|
|
308
325
|
Output structure:
|
|
309
326
|
```yaml
|
|
310
327
|
Backend:
|
|
311
328
|
title: Backend
|
|
329
|
+
category: Backend
|
|
330
|
+
subcategory: null
|
|
312
331
|
icon: code
|
|
313
332
|
order: 1
|
|
314
333
|
items:
|
|
315
334
|
- name: Ruby
|
|
316
335
|
level: 90
|
|
317
336
|
years: 10
|
|
337
|
+
description: null
|
|
338
|
+
icon: null
|
|
339
|
+
color: blue
|
|
340
|
+
featured: true
|
|
341
|
+
order: 1
|
|
342
|
+
id: abc123
|
|
318
343
|
```
|
|
319
344
|
|
|
320
345
|
#### `grouped_by`
|
data/docs/TASKS.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Tasks
|
|
2
|
+
|
|
3
|
+
## To do
|
|
4
|
+
|
|
5
|
+
### Rollup with a single value returns a scalar instead of an array
|
|
6
|
+
|
|
7
|
+
`PropertyExtractors.extract_rollup_array` returns the value alone when a rollup has one item, and an
|
|
8
|
+
array otherwise (since 1.0.1). A property meant to be a list, such as the `Skills` of an experience,
|
|
9
|
+
is then a string for items with a single skill.
|
|
10
|
+
|
|
11
|
+
Templates that iterate with Liquid do not notice, but anything that expects an array breaks: for
|
|
12
|
+
example `{{ experience.skills | jsonify }}` followed by `.map()` in JavaScript fails with
|
|
13
|
+
`skills.slice(...).map is not a function`.
|
|
14
|
+
|
|
15
|
+
**Proposal (1.1.0):** add an opt-in option on the property, so existing sites keep their behavior:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
properties:
|
|
19
|
+
- { name: Skills, type: rollup, array: true }
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
With `array: true`, the extractor always returns an array: `[]` when empty, `[value]` for a single
|
|
23
|
+
value. Document it in the README and `docs/EXAMPLES_AND_CONFIGURATION.md`, and cover the three cases
|
|
24
|
+
(empty, one value, several values) in `property_extractors_spec.rb`.
|
|
25
|
+
|
|
26
|
+
A default change (always an array) would be a breaking change and belongs to 2.0.
|
|
@@ -62,51 +62,99 @@ module JekyllNotionCMS
|
|
|
62
62
|
items_by_category = {}
|
|
63
63
|
|
|
64
64
|
notion_data['results'].each do |page|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
category_color = PropertyExtractors.extract(properties, 'Color', 'rollup')
|
|
77
|
-
category_order = PropertyExtractors.extract(properties, 'Category Order', 'rollup')
|
|
78
|
-
|
|
79
|
-
items_by_category[category_name] ||= {
|
|
80
|
-
'title' => category_name,
|
|
81
|
-
'category' => category_name,
|
|
82
|
-
'subcategory' => nil,
|
|
83
|
-
'icon' => category_icon,
|
|
84
|
-
'order' => category_order || 999,
|
|
85
|
-
'items' => []
|
|
86
|
-
}
|
|
65
|
+
process_category_page(page, items_by_category)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sort_categories_and_items(items_by_category)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Process a single page for category organization
|
|
72
|
+
def process_category_page(page, items_by_category)
|
|
73
|
+
properties = page['properties']
|
|
74
|
+
name = PropertyExtractors.extract(properties, 'Name', 'title')
|
|
75
|
+
return if name.nil? || name.empty?
|
|
87
76
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
77
|
+
split_categories(extract_category_data(properties)).each do |category_data|
|
|
78
|
+
category_name = category_data[:name]
|
|
79
|
+
|
|
80
|
+
items_by_category[category_name] ||= build_category_hash(category_data)
|
|
81
|
+
items_by_category[category_name]['items'] << build_category_item(page, properties, name, category_data)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# An item related to several categories gets arrays from its rollups.
|
|
86
|
+
# Split them into one entry per category, pairing each name with its own icon, color and order.
|
|
87
|
+
def split_categories(category_data)
|
|
88
|
+
names = category_data[:name]
|
|
89
|
+
return [category_data] unless names.is_a?(Array) && names.size > 1
|
|
90
|
+
|
|
91
|
+
names.each_with_index.map do |category_name, index|
|
|
92
|
+
{
|
|
93
|
+
name: category_name,
|
|
94
|
+
icon: value_for_category(category_data[:icon], index, names.size),
|
|
95
|
+
color: value_for_category(category_data[:color], index, names.size),
|
|
96
|
+
order: value_for_category(category_data[:order], index, names.size)
|
|
98
97
|
}
|
|
99
98
|
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Rollups drop empty values, so an array only lines up with the categories when the sizes match
|
|
102
|
+
def value_for_category(value, index, category_count)
|
|
103
|
+
return value unless value.is_a?(Array)
|
|
104
|
+
|
|
105
|
+
value.size == category_count ? value[index] : nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Extract category-related data from properties
|
|
109
|
+
def extract_category_data(properties)
|
|
110
|
+
{
|
|
111
|
+
name: PropertyExtractors.extract(properties, 'Category', 'rollup') || 'Other',
|
|
112
|
+
icon: PropertyExtractors.extract(properties, 'Icon', 'rollup'),
|
|
113
|
+
color: PropertyExtractors.extract(properties, 'Color', 'rollup'),
|
|
114
|
+
order: PropertyExtractors.extract(properties, 'Category Order', 'rollup')
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Build the category hash structure
|
|
119
|
+
def build_category_hash(category_data)
|
|
120
|
+
order = category_data[:order]
|
|
121
|
+
{
|
|
122
|
+
'title' => category_data[:name],
|
|
123
|
+
'category' => category_data[:name],
|
|
124
|
+
'subcategory' => nil,
|
|
125
|
+
'icon' => category_data[:icon],
|
|
126
|
+
'order' => (order.is_a?(Array) ? order.first : order) || 999,
|
|
127
|
+
'items' => []
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Build an item hash for category organization
|
|
132
|
+
def build_category_item(page, properties, name, category_data)
|
|
133
|
+
{
|
|
134
|
+
'name' => name,
|
|
135
|
+
'level' => PropertyExtractors.extract(properties, 'Level', 'number'),
|
|
136
|
+
'years' => PropertyExtractors.extract(properties, 'Years', 'number'),
|
|
137
|
+
'description' => nil,
|
|
138
|
+
'icon' => nil,
|
|
139
|
+
'color' => category_data[:color],
|
|
140
|
+
'featured' => PropertyExtractors.extract(properties, 'Featured', 'checkbox'),
|
|
141
|
+
'order' => PropertyExtractors.extract(properties, 'Order', 'number') || 999,
|
|
142
|
+
'id' => page['id']
|
|
143
|
+
}
|
|
144
|
+
end
|
|
100
145
|
|
|
101
|
-
|
|
102
|
-
|
|
146
|
+
# Sort categories and their items
|
|
147
|
+
def sort_categories_and_items(items_by_category)
|
|
148
|
+
sorted = items_by_category.sort_by do |_, data|
|
|
149
|
+
order = data['order']
|
|
150
|
+
(order.is_a?(Array) ? order.first : order).to_i
|
|
151
|
+
end.to_h
|
|
103
152
|
|
|
104
|
-
|
|
105
|
-
items_by_category.each_value do |data|
|
|
153
|
+
sorted.each_value do |data|
|
|
106
154
|
data['items'].sort_by! { |item| item['order'].to_i }
|
|
107
155
|
end
|
|
108
156
|
|
|
109
|
-
|
|
157
|
+
sorted
|
|
110
158
|
end
|
|
111
159
|
|
|
112
160
|
# Organize items grouped by a field
|
|
@@ -9,6 +9,9 @@ module JekyllNotionCMS
|
|
|
9
9
|
safe true
|
|
10
10
|
priority :highest
|
|
11
11
|
|
|
12
|
+
NOTION_HEADER = 'data imported from Notion'
|
|
13
|
+
FALLBACK_HEADER = 'data generated from the Jekyll collection (Notion fallback)'
|
|
14
|
+
|
|
12
15
|
def generate(site)
|
|
13
16
|
@site = site
|
|
14
17
|
@config = site.config['notion'] || {}
|
|
@@ -78,28 +81,22 @@ module JekyllNotionCMS
|
|
|
78
81
|
end
|
|
79
82
|
|
|
80
83
|
# Create a YAML data file
|
|
81
|
-
|
|
84
|
+
# @param source [Symbol] :notion or :fallback, recorded in the header
|
|
85
|
+
def create_data_file(data, file_name, collection_name, source: :notion)
|
|
82
86
|
data_dir = File.join(@site.source, '_data')
|
|
83
87
|
FileUtils.mkdir_p(data_dir)
|
|
84
88
|
|
|
85
89
|
data_file = File.join(data_dir, file_name)
|
|
86
90
|
new_content = data.to_yaml
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
yaml_start = existing_content.index("---\n")
|
|
92
|
-
if yaml_start
|
|
93
|
-
existing_yaml = existing_content[yaml_start..]
|
|
94
|
-
if existing_yaml.strip == new_content.strip
|
|
95
|
-
Jekyll.logger.info 'NotionCMS:', "#{collection_name} data unchanged, skipping"
|
|
96
|
-
return
|
|
97
|
-
end
|
|
98
|
-
end
|
|
92
|
+
if File.exist?(data_file) && yaml_body(File.read(data_file)) == new_content.strip
|
|
93
|
+
Jekyll.logger.info 'NotionCMS:', "#{collection_name} data unchanged, skipping"
|
|
94
|
+
return
|
|
99
95
|
end
|
|
100
96
|
|
|
97
|
+
header = source == :fallback ? FALLBACK_HEADER : NOTION_HEADER
|
|
101
98
|
File.open(data_file, 'w') do |file|
|
|
102
|
-
file.write("# #{collection_name.capitalize}
|
|
99
|
+
file.write("# #{collection_name.capitalize} #{header}\n")
|
|
103
100
|
file.write("# Auto-generated by jekyll-notion-cms - Do not edit manually\n")
|
|
104
101
|
file.write("# Last updated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
105
102
|
file.write(new_content)
|
|
@@ -108,6 +105,29 @@ module JekyllNotionCMS
|
|
|
108
105
|
Jekyll.logger.info 'NotionCMS:', "#{collection_name} written to _data/#{file_name}"
|
|
109
106
|
end
|
|
110
107
|
|
|
108
|
+
# YAML part of a data file, without the comment header.
|
|
109
|
+
# Works for single-line documents such as "--- []" that an empty collection produces.
|
|
110
|
+
def yaml_body(content)
|
|
111
|
+
content.lines.drop_while { |line| line.start_with?('#') || line.strip.empty? }.join.strip
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Without Notion, keep the last data imported from it: Jekyll already loaded the file into site.data
|
|
115
|
+
def keep_notion_data?(data_file, collection_name)
|
|
116
|
+
return false unless notion_data_file?(File.join(@site.source, '_data', data_file))
|
|
117
|
+
|
|
118
|
+
Jekyll.logger.info 'NotionCMS:', "Keeping Notion data in _data/#{data_file} for #{collection_name}"
|
|
119
|
+
true
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# True when the file holds data imported from Notion rather than a previous fallback.
|
|
123
|
+
# Older versions wrote the Notion header for fallback data too, so also look for collection ids.
|
|
124
|
+
def notion_data_file?(data_file)
|
|
125
|
+
return false unless File.exist?(data_file)
|
|
126
|
+
|
|
127
|
+
content = File.read(data_file)
|
|
128
|
+
content.lines.first.to_s.include?(NOTION_HEADER) && !content.match?(/\bid: collection_\d+\b/)
|
|
129
|
+
end
|
|
130
|
+
|
|
111
131
|
# Use fallback for all collections
|
|
112
132
|
def use_all_collections_fallback
|
|
113
133
|
@collections_config.each do |collection_name, config|
|
|
@@ -120,6 +140,9 @@ module JekyllNotionCMS
|
|
|
120
140
|
Jekyll.logger.info 'NotionCMS:', "Using fallback for #{collection_name}"
|
|
121
141
|
|
|
122
142
|
data_file = config['data_file']
|
|
143
|
+
|
|
144
|
+
return if keep_notion_data?(data_file, collection_name)
|
|
145
|
+
|
|
123
146
|
data_key = data_file.sub('.yml', '').sub('.yaml', '')
|
|
124
147
|
properties_config = config['properties'] || []
|
|
125
148
|
|
|
@@ -137,7 +160,7 @@ module JekyllNotionCMS
|
|
|
137
160
|
|
|
138
161
|
organized_data = DataOrganizers.organize(mock_data, config)
|
|
139
162
|
@site.data[data_key] = organized_data
|
|
140
|
-
create_data_file(organized_data, data_file, collection_name)
|
|
163
|
+
create_data_file(organized_data, data_file, collection_name, source: :fallback)
|
|
141
164
|
|
|
142
165
|
count = organized_data.is_a?(Hash) ? organized_data.size : organized_data.length
|
|
143
166
|
Jekyll.logger.info 'NotionCMS:', "#{collection_name} fallback applied (#{count} items)"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jekyll-notion-cms
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Maxime Lenne
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-26 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: jekyll
|
|
@@ -146,10 +146,9 @@ files:
|
|
|
146
146
|
- Rakefile
|
|
147
147
|
- docs/Download_on_the_App_Store_Badge_FR_RGB_wht_100217.svg
|
|
148
148
|
- docs/EXAMPLES_AND_CONFIGURATION.md
|
|
149
|
+
- docs/TASKS.md
|
|
149
150
|
- docs/architecture.excalidraw
|
|
150
151
|
- docs/architecture.png
|
|
151
|
-
- docs/templates/architecture.excalidraw
|
|
152
|
-
- docs/templates/architecture.png
|
|
153
152
|
- docs/templates/github-actions_notion-sync.yml
|
|
154
153
|
- docs/templates/n8n-workflow_Notion-database-change-trigger-GitHub-Actions.json
|
|
155
154
|
- lib/jekyll-notion-cms.rb
|