sakusei 0.5.8 → 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 +4 -4
- data/Gemfile.lock +1 -1
- data/bin/sakusei-preview +2 -2
- data/examples/meridian-proposal.md +0 -2
- data/examples/meridian-proposal.pdf +0 -0
- data/lib/sakusei/builder.rb +13 -4
- data/lib/sakusei/cli.rb +3 -14
- data/lib/sakusei/converter_base.rb +7 -0
- data/lib/sakusei/multi_file_builder.rb +6 -5
- data/lib/sakusei/version.rb +1 -1
- data/lib/sakusei/vue_processor.rb +1 -1
- data/lib/sakusei.rb +22 -0
- data/sakusei.gemspec +8 -1
- metadata +3 -3
- /data/lib/sakusei/{vue_renderer.js → vue_renderer.cjs} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 51f82cf68852e0f6c9e8ada98c58a804de32dabb7511e5c390c24cbea42e9c4b
|
|
4
|
+
data.tar.gz: 4c4f527519dcd4bba30cd39946c6605e8ea1701594f50e6139509200241cda50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a6e76264d45720805eb3aaea1b2267bd58e5cb7dd980ee2f53fa6e42dbfffa2cff42c6e6ce9004ab51ea811b139e375511530c6bb8fa63a32fd0101bf83fceb1
|
|
7
|
+
data.tar.gz: f27568e7685b2c0f28fe3938fe1bf865d15a6b5ad250758a659c565b66e8bd538a929ac73fb34caf9ab6e7c9430e0dea7bb50064125c38c223c2e14192e3e07a
|
data/Gemfile.lock
CHANGED
data/bin/sakusei-preview
CHANGED
|
@@ -24,9 +24,9 @@ if args.empty?
|
|
|
24
24
|
exit 1
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
-
source_file = args.first
|
|
27
|
+
source_file = Sakusei.resolve_file_extension(args.first)
|
|
28
28
|
unless File.exist?(source_file)
|
|
29
|
-
warn "File not found: #{
|
|
29
|
+
warn "File not found: #{args.first}"
|
|
30
30
|
exit 1
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
/>
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
Report Generated <%= today %>
|
|
12
|
-
|
|
13
11
|
## Opportunity
|
|
14
12
|
|
|
15
13
|
Meridian Property Group manages a portfolio of 340 residential and commercial tenancies across Southeast Queensland. The business runs on strong fundamentals, but operational bandwidth is a growing constraint: leasing coordinators spend the majority of their week on routine correspondence, manual data entry, and chasing approvals through email chains. This is work that AI and automation can largely eliminate.
|
|
Binary file
|
data/lib/sakusei/builder.rb
CHANGED
|
@@ -53,11 +53,16 @@ module Sakusei
|
|
|
53
53
|
style_pack = discover_style_pack
|
|
54
54
|
$stderr.puts "[sakusei] style pack: #{style_pack.name} (#{style_pack.path})"
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
if sandboxed?
|
|
57
|
+
$stderr.puts "[sakusei] sandboxed: skipping file includes and ERB"
|
|
58
|
+
processed_content = File.read(@source_file)
|
|
59
|
+
else
|
|
60
|
+
$stderr.puts "[sakusei] resolving file includes..."
|
|
61
|
+
resolved_content = resolve_files
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
$stderr.puts "[sakusei] processing ERB..."
|
|
64
|
+
processed_content = process_erb(resolved_content)
|
|
65
|
+
end
|
|
61
66
|
|
|
62
67
|
processed_content = expand_break_syntax(processed_content)
|
|
63
68
|
processed_content = process_vue(processed_content, style_pack)
|
|
@@ -66,6 +71,10 @@ module Sakusei
|
|
|
66
71
|
[style_pack, processed_content]
|
|
67
72
|
end
|
|
68
73
|
|
|
74
|
+
def sandboxed?
|
|
75
|
+
!!@options[:sandboxed]
|
|
76
|
+
end
|
|
77
|
+
|
|
69
78
|
def discover_style_pack
|
|
70
79
|
StylePack.discover(@options[:source_dir] || @source_dir, @options[:style])
|
|
71
80
|
end
|
data/lib/sakusei/cli.rb
CHANGED
|
@@ -24,6 +24,7 @@ module Sakusei
|
|
|
24
24
|
option :stylesheet, aliases: '-css', desc: 'Path to CSS stylesheet'
|
|
25
25
|
option :page_breaks, aliases: '-p', type: :boolean, default: false, desc: 'Add page breaks between files'
|
|
26
26
|
option :open, type: :boolean, default: false, desc: 'Open the PDF after building'
|
|
27
|
+
option :sandboxed, type: :boolean, default: false, desc: 'Treat input as inert markdown: no ERB, no @include, images restricted to the source directory'
|
|
27
28
|
def build(*files)
|
|
28
29
|
raise Error, 'No input files provided' if files.empty?
|
|
29
30
|
|
|
@@ -242,26 +243,14 @@ module Sakusei
|
|
|
242
243
|
return false if arg.nil? || arg.empty?
|
|
243
244
|
return false if File.extname(arg).length > 0 # Already has an extension
|
|
244
245
|
|
|
245
|
-
|
|
246
|
+
Sakusei::MARKDOWN_EXTENSIONS.any? { |ext| File.exist?(arg + ext) }
|
|
246
247
|
end
|
|
247
248
|
|
|
248
249
|
private
|
|
249
250
|
|
|
250
251
|
# Resolve file by trying markdown extensions if no extension provided
|
|
251
252
|
def resolve_file_extension(file)
|
|
252
|
-
|
|
253
|
-
return file if File.directory?(file)
|
|
254
|
-
return file if file.include?('*') # Glob pattern
|
|
255
|
-
return file if File.extname(file).length > 0 # Already has extension
|
|
256
|
-
|
|
257
|
-
# Try markdown extensions
|
|
258
|
-
%w[.md .text .markdown].each do |ext|
|
|
259
|
-
path_with_ext = file + ext
|
|
260
|
-
return path_with_ext if File.exist?(path_with_ext)
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
# Return original if no extension found
|
|
264
|
-
file
|
|
253
|
+
Sakusei.resolve_file_extension(file)
|
|
265
254
|
end
|
|
266
255
|
|
|
267
256
|
def open_pdf(path)
|
|
@@ -18,6 +18,9 @@ module Sakusei
|
|
|
18
18
|
|
|
19
19
|
image_paths.each do |rel_path|
|
|
20
20
|
src = File.expand_path(rel_path, @source_dir)
|
|
21
|
+
# Sandboxed builds must not read files outside the source directory
|
|
22
|
+
# (image_paths already excludes absolute paths; this blocks ../ escapes).
|
|
23
|
+
next if sandboxed? && !src.start_with?(File.expand_path(@source_dir) + File::SEPARATOR)
|
|
21
24
|
next unless File.exist?(src)
|
|
22
25
|
|
|
23
26
|
dest = File.join(temp_dir, rel_path)
|
|
@@ -26,6 +29,10 @@ module Sakusei
|
|
|
26
29
|
end
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
def sandboxed?
|
|
33
|
+
!!@options[:sandboxed]
|
|
34
|
+
end
|
|
35
|
+
|
|
29
36
|
def image_paths
|
|
30
37
|
paths = []
|
|
31
38
|
@content.scan(/!\[[^\]]*\]\(([^)]+)\)/) { |m| paths << m[0].strip }
|
|
@@ -60,11 +60,12 @@ module Sakusei
|
|
|
60
60
|
parts = []
|
|
61
61
|
|
|
62
62
|
@files.each do |file|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
resolved = if @options[:sandboxed]
|
|
64
|
+
File.read(file)
|
|
65
|
+
else
|
|
66
|
+
# Resolve includes within each file
|
|
67
|
+
FileResolver.new(file).resolve
|
|
68
|
+
end
|
|
68
69
|
|
|
69
70
|
parts << resolved
|
|
70
71
|
|
data/lib/sakusei/version.rb
CHANGED
data/lib/sakusei.rb
CHANGED
|
@@ -21,6 +21,28 @@ require_relative 'sakusei/page_chrome_translator'
|
|
|
21
21
|
module Sakusei
|
|
22
22
|
class Error < StandardError; end
|
|
23
23
|
|
|
24
|
+
# Markdown extensions to auto-discover when a CLI is given a file argument
|
|
25
|
+
# without an extension (e.g. `sakusei-preview ai_workflow_assessment`).
|
|
26
|
+
MARKDOWN_EXTENSIONS = %w[.md .text .markdown].freeze
|
|
27
|
+
|
|
28
|
+
# Resolve a file argument by trying known markdown extensions if no extension
|
|
29
|
+
# was given. Returns the input unchanged if the file already exists, is a
|
|
30
|
+
# directory, looks like a glob, or already has an extension.
|
|
31
|
+
def self.resolve_file_extension(file)
|
|
32
|
+
return file if file.nil? || file.empty?
|
|
33
|
+
return file if File.exist?(file)
|
|
34
|
+
return file if File.directory?(file)
|
|
35
|
+
return file if file.include?('*')
|
|
36
|
+
return file if File.extname(file).length.positive?
|
|
37
|
+
|
|
38
|
+
MARKDOWN_EXTENSIONS.each do |ext|
|
|
39
|
+
candidate = file + ext
|
|
40
|
+
return candidate if File.exist?(candidate)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
file
|
|
44
|
+
end
|
|
45
|
+
|
|
24
46
|
# Main entry point for building PDFs
|
|
25
47
|
def self.build(source_file, options = {})
|
|
26
48
|
Builder.new(source_file, options).build
|
data/sakusei.gemspec
CHANGED
|
@@ -14,7 +14,14 @@ Gem::Specification.new do |spec|
|
|
|
14
14
|
spec.required_ruby_version = '>= 3.0.0'
|
|
15
15
|
|
|
16
16
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
17
|
-
|
|
17
|
+
if File.directory?('.git') && system('git --version', out: File::NULL, err: File::NULL)
|
|
18
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
|
19
|
+
else
|
|
20
|
+
# No git (e.g. vendored via `git archive` inside a Docker image) —
|
|
21
|
+
# glob the shipped tree instead.
|
|
22
|
+
Dir.glob('{lib,bin,examples}/**/*', File::FNM_DOTMATCH).select { |f| File.file?(f) } +
|
|
23
|
+
Dir.glob('*').select { |f| File.file?(f) }
|
|
24
|
+
end
|
|
18
25
|
end
|
|
19
26
|
spec.bindir = 'bin'
|
|
20
27
|
spec.executables = %w[sakusei sakusei-preview]
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sakusei
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keith Rowell
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: thor
|
|
@@ -137,7 +137,7 @@ files:
|
|
|
137
137
|
- lib/sakusei/style_preview.rb
|
|
138
138
|
- lib/sakusei/version.rb
|
|
139
139
|
- lib/sakusei/vue_processor.rb
|
|
140
|
-
- lib/sakusei/vue_renderer.
|
|
140
|
+
- lib/sakusei/vue_renderer.cjs
|
|
141
141
|
- lib/templates/base.css
|
|
142
142
|
- lib/templates/default_style_pack/.gitignore
|
|
143
143
|
- lib/templates/default_style_pack/components/HelloWorld.vue
|
|
File without changes
|