raindown 0.16.0 → 0.17.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 +4 -4
- data/lib/raindown/chart/points.rb +2 -0
- data/lib/raindown/cli.rb +13 -5
- data/lib/raindown/front_matter.rb +10 -0
- data/lib/raindown/list.rb +12 -4
- data/lib/raindown/pages.rb +5 -4
- data/lib/raindown/projects.rb +12 -0
- data/lib/raindown/skeleton.rb +114 -0
- data/lib/raindown/table/markdown_table.rb +6 -1
- data/lib/raindown/table/table.rb +20 -10
- data/lib/raindown/utils.rb +5 -0
- data/lib/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2abf28897b1c392d34ec786d7abad550414050fe3f45949929240fa31453cf23
|
|
4
|
+
data.tar.gz: edafdc330bddeaf1b76a4d6538b1e3db9241c1758526d0076b0ffe76d5aac32a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ce17ac9171dfa2d3a37311698f53e89390eccd2d3105a60adef58136f6bf4f741162ecd6eea0edc10a341434aaa58b1b8832e1971cc1ae3498771255616e3ce
|
|
7
|
+
data.tar.gz: 15ff81a860a3403f8bca98ca63052be5d8ff1cc9feda5da235f6b753ffd92f74ba313471ac60d7b61c59fb7c84f38bd05e8dd7dfa68877a66d84bd5251499217
|
|
@@ -5,6 +5,7 @@ require 'json'
|
|
|
5
5
|
require 'yaml'
|
|
6
6
|
|
|
7
7
|
require_relative '../node'
|
|
8
|
+
require_relative '../utils'
|
|
8
9
|
|
|
9
10
|
module Raindown
|
|
10
11
|
class Chart < Node
|
|
@@ -43,6 +44,7 @@ module Raindown
|
|
|
43
44
|
return numbers(numbers) if numbers.any? && numbers.all? { number(it) }
|
|
44
45
|
|
|
45
46
|
path = File.expand_path(string, @root)
|
|
47
|
+
raise Error, "it's outside the project" unless Utils.inside?(path, @root)
|
|
46
48
|
raise Error, 'no such file' unless File.file?(path)
|
|
47
49
|
|
|
48
50
|
from(read(path))
|
data/lib/raindown/cli.rb
CHANGED
|
@@ -10,6 +10,7 @@ require_relative 'blueprints'
|
|
|
10
10
|
require_relative 'editing'
|
|
11
11
|
require_relative 'rewriter'
|
|
12
12
|
require_relative 'server'
|
|
13
|
+
require_relative 'skeleton'
|
|
13
14
|
require_relative 'watcher/file_watcher'
|
|
14
15
|
require_relative 'watcher/supervisor'
|
|
15
16
|
|
|
@@ -32,6 +33,8 @@ module Raindown
|
|
|
32
33
|
PATH is a file, or a directory (default: current directory) whose projects with a
|
|
33
34
|
.raindown folder are watched
|
|
34
35
|
init [DIRECTORY] Create a .raindown folder for the project's config and nodes (default: current directory)
|
|
36
|
+
skeleton Set this repo up to commit directives without what they write, in files with "skeleton: true"
|
|
37
|
+
skeleton --check Exit with 1, naming any such file with output staged, for CI
|
|
35
38
|
server Keep Raindown loaded for an editor, answering a JSON request a line on stdin with its HTML,
|
|
36
39
|
or its Markdown as it would be written back, like --stdin and --stdin --rewrite
|
|
37
40
|
blueprints Save the blueprints in Claude Code's last reply to .raindown/data/blueprints.md, for the terminal
|
|
@@ -78,17 +81,22 @@ module Raindown
|
|
|
78
81
|
def self.run(argv)
|
|
79
82
|
# Markdown is UTF-8 whatever the locale, as an app opened from the Dock, like Obsidian, starts Raindown without one.
|
|
80
83
|
Encoding.default_external = Encoding::UTF_8
|
|
81
|
-
return if command(argv)
|
|
82
|
-
|
|
83
|
-
# Without a path, like "raindown", "raindown -x" or "raindown watch", it's the current directory.
|
|
84
|
-
argv = ['watch', '.', *argv.drop(1)] if argv.first == 'watch' && (argv.size == 1 || argv[1].start_with?('-'))
|
|
85
|
-
argv = ['.', *argv] if argv.empty? || argv.first.start_with?('-')
|
|
84
|
+
return if command(argv) || Skeleton.run_command(argv)
|
|
86
85
|
|
|
86
|
+
argv = with_path(argv)
|
|
87
87
|
super || abort("Unknown arguments: #{argv.join(' ')}\n\n#{USAGE}")
|
|
88
88
|
rescue StandardError => e
|
|
89
89
|
report(e, path: argv.first)
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
# Without a path, like "raindown", "raindown -x" or "raindown watch", it's the current directory.
|
|
93
|
+
def self.with_path(argv)
|
|
94
|
+
return ['watch', '.', *argv.drop(1)] if argv.first == 'watch' && (argv.size == 1 || argv[1].start_with?('-'))
|
|
95
|
+
return ['.', *argv] if argv.empty? || argv.first.start_with?('-')
|
|
96
|
+
|
|
97
|
+
argv
|
|
98
|
+
end
|
|
99
|
+
|
|
92
100
|
# The :path line would match help, version and init as the file, so they're checked first.
|
|
93
101
|
# Returns true when argv was one of them.
|
|
94
102
|
def self.command(argv)
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require 'date'
|
|
4
4
|
require 'yaml'
|
|
5
5
|
|
|
6
|
+
require_relative 'utils'
|
|
7
|
+
|
|
6
8
|
module Raindown
|
|
7
9
|
# YAML between two --- lines at the very start of a file.
|
|
8
10
|
module FrontMatter
|
|
@@ -19,6 +21,14 @@ module Raindown
|
|
|
19
21
|
[metadata.is_a?(Hash) ? metadata : {}, match.post_match]
|
|
20
22
|
end
|
|
21
23
|
|
|
24
|
+
# A file's metadata, or none when it can't be read, with a warning naming it, so one file can't stop a list of them.
|
|
25
|
+
def read(file_path)
|
|
26
|
+
parse(Utils.read_markdown(file_path)).first
|
|
27
|
+
rescue Psych::Exception, ArgumentError => e
|
|
28
|
+
warn "raindown: #{file_path}: #{e.message}, so it's listed without its front matter"
|
|
29
|
+
{}
|
|
30
|
+
end
|
|
31
|
+
|
|
22
32
|
# How many of the lines are front matter, so directives in its YAML are left alone.
|
|
23
33
|
def line_count(lines)
|
|
24
34
|
lines.join.match(PATTERN)&.then { |match| match[0].lines.length } || 0
|
data/lib/raindown/list.rb
CHANGED
|
@@ -14,18 +14,26 @@ module Raindown
|
|
|
14
14
|
@tags = tags
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
# Each file has its front matter, its path and its content. Sorted by an order,
|
|
18
|
-
# with the same value stay in the order they're listed.
|
|
17
|
+
# Each file has its front matter, its path and its content. Sorted by an order, numbers come first, then other values
|
|
18
|
+
# as text, then files without it, and ones with the same value stay in the order they're listed.
|
|
19
19
|
def files(metadata: NoMetadata)
|
|
20
20
|
files = metadata.list(**@tags)
|
|
21
21
|
return files unless @order
|
|
22
22
|
|
|
23
23
|
sorted, unsorted = files.partition { it[@order] }
|
|
24
|
-
sorted.sort_by.with_index { |file, index| [file[@order], index] } + unsorted
|
|
24
|
+
sorted.sort_by.with_index { |file, index| [*order_key(file[@order]), index] } + unsorted
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def rewrite(metadata: NoMetadata)
|
|
28
|
-
files(metadata:).map { |file| "- [#{file.title || file.path}](#{file.path})" }.join("\n")
|
|
28
|
+
files(metadata:).map { |file| "- [#{file.title || File.basename(file.path, '.*')}](#{file.path})" }.join("\n")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def order_key(value)
|
|
34
|
+
return [0, value, ''] if value.is_a?(Numeric)
|
|
35
|
+
|
|
36
|
+
[1, 0, value.to_s]
|
|
29
37
|
end
|
|
30
38
|
end
|
|
31
39
|
end
|
data/lib/raindown/pages.rb
CHANGED
|
@@ -25,10 +25,11 @@ module Raindown
|
|
|
25
25
|
@front_matters ||= {}
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# The Markdown files under a project's root, the folder with its .raindown folder or else the file's
|
|
28
|
+
# The Markdown files under a project's root, the folder with its .raindown folder, or a vault, or else the file's
|
|
29
|
+
# folder.
|
|
29
30
|
def for(path)
|
|
30
|
-
|
|
31
|
-
load(root:
|
|
31
|
+
require_relative 'projects'
|
|
32
|
+
load(root: Projects.content_root(path) || File.dirname(File.expand_path(path)))
|
|
32
33
|
end
|
|
33
34
|
|
|
34
35
|
# Lists the files under the root, without loading them, for Dirload.
|
|
@@ -118,7 +119,7 @@ module Raindown
|
|
|
118
119
|
modified_then, front_matter = self.class.front_matters[file_path]
|
|
119
120
|
return front_matter if modified_then == modified
|
|
120
121
|
|
|
121
|
-
FrontMatter.
|
|
122
|
+
FrontMatter.read(file_path).tap { self.class.front_matters[file_path] = [modified, it] }
|
|
122
123
|
end
|
|
123
124
|
|
|
124
125
|
# The path is relative to the root, like docs/_basics/_1-getting-started.md.
|
data/lib/raindown/projects.rb
CHANGED
|
@@ -44,6 +44,18 @@ module Raindown
|
|
|
44
44
|
folder && File.dirname(folder)
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# The folder a file's paths are relative to: the nearest with a .raindown folder or that's a vault, or nil without one.
|
|
48
|
+
# A vault doesn't need a .raindown folder, as Obsidian's links and a table's path are from its top.
|
|
49
|
+
def content_root(path)
|
|
50
|
+
folder = File.dirname(File.expand_path(path))
|
|
51
|
+
until project?(folder) || vault?(folder)
|
|
52
|
+
return if folder == File.dirname(folder)
|
|
53
|
+
|
|
54
|
+
folder = File.dirname(folder)
|
|
55
|
+
end
|
|
56
|
+
folder
|
|
57
|
+
end
|
|
58
|
+
|
|
47
59
|
# The project's Markdown files, sorted, leaving out ignored folders. The projects inside it are left out too,
|
|
48
60
|
# since they have nodes of their own, and vaults, which their editor writes back to.
|
|
49
61
|
def files(root)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'open3'
|
|
4
|
+
|
|
5
|
+
require_relative 'bare_directives'
|
|
6
|
+
require_relative 'document'
|
|
7
|
+
require_relative 'front_matter'
|
|
8
|
+
require_relative 'nodes'
|
|
9
|
+
require_relative 'pages'
|
|
10
|
+
require_relative 'utils'
|
|
11
|
+
|
|
12
|
+
module Raindown
|
|
13
|
+
# A file with "skeleton: true" in its front matter keeps its directives in git, but not what they write, so dashboards
|
|
14
|
+
# don't fill its history and sensitive reports stay out of it. It's a git clean filter: the file on disk keeps its
|
|
15
|
+
# output, and what's committed doesn't, which a clone writes again when it's rendered. The filter's required, so when
|
|
16
|
+
# a file can't be stripped, git stops rather than commit its output.
|
|
17
|
+
module Skeleton
|
|
18
|
+
FILTER = 'raindown'
|
|
19
|
+
SKELETON = /^skeleton:[ \t]*true[ \t]*$/
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Returns true when the arguments were "raindown skeleton", with --check or a file or neither.
|
|
24
|
+
def run_command(argv)
|
|
25
|
+
return false unless argv.first == 'skeleton' && argv.size <= 2
|
|
26
|
+
|
|
27
|
+
run(argv[1])
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# "raindown skeleton" sets the repo up, --check is for CI, and with a file it's git's filter.
|
|
32
|
+
def run(argument)
|
|
33
|
+
root, = Open3.capture2('git', 'rev-parse', '--show-toplevel')
|
|
34
|
+
abort('raindown skeleton needs a git repo') if root.strip.empty?
|
|
35
|
+
return install(root.strip) unless argument
|
|
36
|
+
return check(root.strip) if argument == '--check'
|
|
37
|
+
|
|
38
|
+
filter(argument)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# From stdin to stdout, which it has to itself, so what nodes print goes to stderr, like the server's answers.
|
|
42
|
+
def filter(path)
|
|
43
|
+
output = $stdout.dup
|
|
44
|
+
$stdout.reopen($stderr)
|
|
45
|
+
output.write(strip($stdin.read, path:))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def check(root)
|
|
49
|
+
files = staged_output(root)
|
|
50
|
+
return if files.empty?
|
|
51
|
+
|
|
52
|
+
warn('Output is staged in these skeleton files. Run "raindown skeleton", then "git add --renormalize .":', *files)
|
|
53
|
+
exit 1
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The Markdown without what its directives wrote, or as it is, to the byte, without "skeleton: true".
|
|
57
|
+
def strip(markdown, path:)
|
|
58
|
+
return markdown unless skeleton?(markdown)
|
|
59
|
+
|
|
60
|
+
FrontMatter.parse(markdown) # Raises when it can't be read, as bare directives would be missed.
|
|
61
|
+
Nodes.load(path)
|
|
62
|
+
markdown = markdown.gsub("\r\n", "\n")
|
|
63
|
+
lines = markdown.lines
|
|
64
|
+
outputs(markdown, path).reverse_each { |output| lines.slice!((output.begin - 1)..(output.end - 1)) }
|
|
65
|
+
lines.join
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Sets the filter up for a repo. Git keeps a filter's command in its own config, so each clone needs this.
|
|
69
|
+
def install(root)
|
|
70
|
+
{ clean: "#{FILTER} skeleton %f", smudge: 'cat', required: 'true' }.each do |name, value|
|
|
71
|
+
system('git', 'config', "filter.#{FILTER}.#{name}", value, chdir: root, exception: true)
|
|
72
|
+
end
|
|
73
|
+
add_attributes(File.join(root, '.gitattributes'))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The staged files that still have output, like ones added before the filter was set up, or in a clone without it.
|
|
77
|
+
def staged_output(root)
|
|
78
|
+
tracked(root).reject do |path|
|
|
79
|
+
staged = staged_markdown(root, path)
|
|
80
|
+
strip(staged, path: File.join(root, path)) == staged
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Read from the front matter's text, so a file with front matter that isn't valid YAML, or isn't UTF-8, can still be
|
|
85
|
+
# committed when it isn't a skeleton.
|
|
86
|
+
def skeleton?(markdown)
|
|
87
|
+
markdown.scrub.match(FrontMatter::PATTERN)&.[](1)&.match?(SKELETON) || false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def outputs(markdown, path)
|
|
91
|
+
bare = BareDirectives.enabled?(path:, content: markdown)
|
|
92
|
+
Document.new(markdown:, bare:).directives.select(&:writes).filter_map(&:output)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def add_attributes(path)
|
|
96
|
+
existing = File.exist?(path) ? File.read(path) : ''
|
|
97
|
+
missing = Pages::EXTENSIONS.map { "*.#{it} filter=#{FILTER}" }.reject { existing.include?(it) }
|
|
98
|
+
return if missing.empty?
|
|
99
|
+
|
|
100
|
+
existing += "\n" unless existing.empty? || existing.end_with?("\n")
|
|
101
|
+
File.write(path, "#{existing}#{missing.join("\n")}\n")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def tracked(root)
|
|
105
|
+
output, = Open3.capture2('git', 'ls-files', '-z', chdir: root)
|
|
106
|
+
output.split("\0").select { Pages::EXTENSIONS.include?(File.extname(it).delete_prefix('.').downcase) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def staged_markdown(root, path)
|
|
110
|
+
output, = Open3.capture2('git', 'show', ":#{path}", chdir: root)
|
|
111
|
+
output.force_encoding(Encoding::UTF_8)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -35,7 +35,12 @@ module Raindown
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
def escape(cell)
|
|
38
|
-
cell.gsub('|', '\|')
|
|
38
|
+
one_line(cell).gsub('|', '\|')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# The cell as a table shows it, on one line.
|
|
42
|
+
def one_line(cell)
|
|
43
|
+
cell.gsub(/\s*\R\s*/, ' ')
|
|
39
44
|
end
|
|
40
45
|
end
|
|
41
46
|
end
|
data/lib/raindown/table/table.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'digest'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
|
|
7
7
|
require_relative '../node'
|
|
8
|
+
require_relative '../utils'
|
|
8
9
|
require_relative 'markdown_table'
|
|
9
10
|
require_relative 'table_formulas'
|
|
10
11
|
require_relative 'written_table'
|
|
@@ -36,24 +37,31 @@ module Raindown
|
|
|
36
37
|
# Written is what it wrote last time, so it can tell which of the table and the file changed since.
|
|
37
38
|
def rewrite(written: [], metadata: NoMetadata)
|
|
38
39
|
path = data_path(metadata)
|
|
39
|
-
data_error(path) || synced(path, written.map(&:chomp))
|
|
40
|
+
data_error(path, metadata) || synced(path, written.map(&:chomp))
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
# Write-in, in <{ }>, the table only shows its file, and never writes to it.
|
|
43
44
|
def render(metadata: NoMetadata)
|
|
44
45
|
path = data_path(metadata)
|
|
45
|
-
Raindown.commonmark(data_error(path) || MarkdownTable.format(with_total(TableFormulas.evaluate(read_rows(path)))))
|
|
46
|
+
Raindown.commonmark(data_error(path, metadata) || MarkdownTable.format(with_total(TableFormulas.evaluate(read_rows(path)))))
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
private
|
|
49
50
|
|
|
50
51
|
def data_path(metadata)
|
|
51
|
-
File.expand_path(@data.to_s,
|
|
52
|
+
File.expand_path(@data.to_s, data_root(metadata))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def data_root(metadata)
|
|
56
|
+
return metadata.root if metadata.respond_to?(:root)
|
|
57
|
+
|
|
58
|
+
Dir.pwd
|
|
52
59
|
end
|
|
53
60
|
|
|
54
61
|
# Why the file can't be a table, or nil when it can.
|
|
55
|
-
def data_error(path)
|
|
62
|
+
def data_error(path, metadata)
|
|
56
63
|
return "Can't show #{@data.inspect} as a table: only CSV and TSV files can be" unless SEPARATORS.key?(File.extname(path).downcase)
|
|
64
|
+
return "Can't show #{@data.inspect} as a table: it's outside the project" unless Utils.inside?(path, data_root(metadata))
|
|
57
65
|
|
|
58
66
|
"Can't show #{@data.inspect} as a table: no such file" unless File.file?(path)
|
|
59
67
|
end
|
|
@@ -64,7 +72,7 @@ module Raindown
|
|
|
64
72
|
synced_hash = written.first&.[](COMMENT, 1)
|
|
65
73
|
table = WrittenTable.new(written_rows(written), file)
|
|
66
74
|
return markdown(file, table.revealed) if unchanged?(table.rows, synced_hash, shown)
|
|
67
|
-
return markdown(write_rows(path,
|
|
75
|
+
return markdown(write_rows(path, with_file_cells(table.rows, file, shown)), table.revealed) if hash_rows(shown) == synced_hash
|
|
68
76
|
|
|
69
77
|
warn "raindown: #{@data} and its table both changed, so neither is written until they match"
|
|
70
78
|
["<!-- table conflict · #{synced_hash} -->", *written.drop(1)].join("\n")
|
|
@@ -95,12 +103,13 @@ module Raindown
|
|
|
95
103
|
revealed.each_with_object(values.map(&:dup)) { |(row, column), shown| shown[row][column] = rows[row][column] }
|
|
96
104
|
end
|
|
97
105
|
|
|
98
|
-
# The table's rows, keeping the file's
|
|
99
|
-
|
|
106
|
+
# The table's rows, keeping the file's cell where the table still shows it, like a formula as its value, or a cell
|
|
107
|
+
# with line breaks on one line.
|
|
108
|
+
def with_file_cells(table, file, shown)
|
|
100
109
|
table.each_with_index.map do |row, row_index|
|
|
101
110
|
row.each_with_index.map do |cell, column|
|
|
102
|
-
|
|
103
|
-
next
|
|
111
|
+
kept = file.dig(row_index, column)
|
|
112
|
+
next kept if kept && MarkdownTable.one_line(shown.dig(row_index, column).to_s) == cell
|
|
104
113
|
|
|
105
114
|
cell
|
|
106
115
|
end
|
|
@@ -146,8 +155,9 @@ module Raindown
|
|
|
146
155
|
rows
|
|
147
156
|
end
|
|
148
157
|
|
|
158
|
+
# As the table shows them, so a cell with line breaks hashes the same in both.
|
|
149
159
|
def hash_rows(rows)
|
|
150
|
-
Digest::SHA256.hexdigest(JSON.generate(rows))[0, 6]
|
|
160
|
+
Digest::SHA256.hexdigest(JSON.generate(rows.map { |row| row.map { MarkdownTable.one_line(it.to_s) } }))[0, 6]
|
|
151
161
|
end
|
|
152
162
|
end
|
|
153
163
|
end
|
data/lib/raindown/utils.rb
CHANGED
|
@@ -4,6 +4,11 @@ module Raindown
|
|
|
4
4
|
module Utils
|
|
5
5
|
module_function
|
|
6
6
|
|
|
7
|
+
# Whether a path is below a folder, so a note can't reach a file outside its project, like '../../secrets.csv'.
|
|
8
|
+
def inside?(path, folder)
|
|
9
|
+
File.expand_path(path).start_with?(File.join(File.expand_path(folder), ''))
|
|
10
|
+
end
|
|
11
|
+
|
|
7
12
|
# A Markdown file's text, with its line endings as LF, "\n", whatever they are in the file.
|
|
8
13
|
#
|
|
9
14
|
# Windows ends lines with CRLF, "\r\n", and Git for Windows checks files out that way, so a file written or cloned on
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raindown
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
@@ -251,6 +251,7 @@ files:
|
|
|
251
251
|
- lib/raindown/projects.rb
|
|
252
252
|
- lib/raindown/rewriter.rb
|
|
253
253
|
- lib/raindown/server.rb
|
|
254
|
+
- lib/raindown/skeleton.rb
|
|
254
255
|
- lib/raindown/stat.rb
|
|
255
256
|
- lib/raindown/table/markdown_table.rb
|
|
256
257
|
- lib/raindown/table/table.rb
|