kamisaku 0.3.4 → 0.4.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/Gemfile.lock +1 -1
  4. data/README.md +9 -63
  5. data/examples/birthday_invitation/dino/invitation.pdf +0 -0
  6. data/lib/kamisaku/arg_parser.rb +4 -0
  7. data/lib/kamisaku/cli_runner.rb +1 -1
  8. data/lib/kamisaku/content_validators/base_content_validator.rb +14 -0
  9. data/lib/kamisaku/content_validators/birthday_invitation_content_validator.rb +218 -0
  10. data/lib/kamisaku/{content_validator.rb → content_validators/resume_content_validator.rb} +10 -7
  11. data/lib/kamisaku/html_builder.rb +4 -3
  12. data/lib/kamisaku/pdf.rb +14 -11
  13. data/lib/kamisaku/template_helpers.rb +0 -10
  14. data/lib/kamisaku/version.rb +1 -1
  15. data/lib/kamisaku.rb +4 -2
  16. data/lib/schema/birthday_invitation/example.yml +45 -0
  17. data/lib/schema/birthday_invitation/schema.yml +40 -0
  18. data/lib/templates/birthday_invitation/dino/template.html.erb +486 -0
  19. metadata +24 -18
  20. /data/examples/{chromatic → resume/chromatic}/example.pdf +0 -0
  21. /data/examples/{gradient → resume/gradient}/example.pdf +0 -0
  22. /data/examples/{meridian → resume/meridian}/example.pdf +0 -0
  23. /data/examples/{paper → resume/paper}/example.pdf +0 -0
  24. /data/examples/{prism → resume/prism}/example.pdf +0 -0
  25. /data/examples/{sleek → resume/sleek}/example.pdf +0 -0
  26. /data/examples/{zenith → resume/zenith}/example.pdf +0 -0
  27. /data/lib/schema/{example.yml → resume/example.yml} +0 -0
  28. /data/lib/schema/{schema.yml → resume/schema.yml} +0 -0
  29. /data/lib/templates/{chromatic → resume/chromatic}/template.html.erb +0 -0
  30. /data/lib/templates/{gradient → resume/gradient}/template.html.erb +0 -0
  31. /data/lib/templates/{meridian → resume/meridian}/template.html.erb +0 -0
  32. /data/lib/templates/{paper → resume/paper}/template.html.erb +0 -0
  33. /data/lib/templates/{prism → resume/prism}/template.html.erb +0 -0
  34. /data/lib/templates/{sleek → resume/sleek}/template.html.erb +0 -0
  35. /data/lib/templates/{zenith → resume/zenith}/template.html.erb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bfceaef01ef3c1d4a92122575d3a8a1eaca248166559c1703b325f5cd5c78ce
4
- data.tar.gz: 740b3acfe49425ca0e41ddeab7b026b880524cbbe06ee40799197d1039fd043d
3
+ metadata.gz: '0307528fa9400ae092638a768482af0a290b9f7e65f0ca8fcccfa501493eeb92'
4
+ data.tar.gz: bec81b49d0f9a635ad59dcfde08f718a5f8ab5471fa5d9c23eaf37dd2a3789e5
5
5
  SHA512:
6
- metadata.gz: 8345ffa22c0407fcb09dca29ee3dd0d44add12e38e73f4dcb9732d576367333533ee19a05fee4aabf936e62628989f78f3ce71452e03ae5c196c6cc6746ac7f3
7
- data.tar.gz: 3aab44e96289cae64acfd4530f92b570e26f4d010a5b639e4f21b6240e2cd4b667d1370426cbf0917a8c31d4fb0adbaabb66613cad48e05c4bbfe0bfd2dcb93d
6
+ metadata.gz: '0738241ce36d4fb6e43439a35e39614ac935e922247cfb91e87dc3e236b6897f105feee6d827e3341f19286344935e7c37ea19e4705b6c7eb5187c6758049c6f'
7
+ data.tar.gz: 0525b51569873f87984eadd9c779b3ac0254250c541e3fbf6cb37e0f14a746ac3866e2024911f94727f6af40f58afd0758ef6c5faddb915534328e638fa0b27f
data/CHANGELOG.md CHANGED
@@ -13,6 +13,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
13
 
14
14
  ### Removed
15
15
 
16
+ ## [0.4.0] - 2025.06.09
17
+
18
+ ### Added
19
+ - Added new `birthday_invitation` schema type for Birthday Invitation PDF generations
20
+
21
+ ### Changed
22
+ - Updated the CLI tool to accept `category` to generate specific PDF
23
+
16
24
  ## [0.3.4] - 2025.06.06
17
25
 
18
26
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kamisaku (0.3.4)
4
+ kamisaku (0.4.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 📃 Kamisaku
2
2
 
3
- *Build a CV PDF from a yaml text file.*
3
+ *Build resume PDF from a yaml text file.*
4
4
 
5
5
  🚀 See it in action at [https://kamisaku.sinaru.com/](https://kamisaku.sinaru.com/?utm_source=github).
6
6
 
@@ -11,7 +11,7 @@
11
11
  See [examples](/examples) directory for sample generated PDF files based of [templates](/lib/templates).
12
12
 
13
13
  ## Templates
14
- For a list of templates availble for CV generation, check the [examples](/examples) directory where each directory name is a template name.
14
+ For a list of templates availble for PDF generation, check the [examples](/examples) directory where each directory name is a template name.
15
15
 
16
16
  ## Installation
17
17
 
@@ -42,64 +42,9 @@ $ gem install kamisaku
42
42
 
43
43
  ## Usage
44
44
 
45
- First we need to have a `yaml` file or a string with the CV data. The gem supports the following sections.
46
-
47
- ```yaml
48
- version: 1
49
- profile:
50
- name: # Your name
51
- title: # Your current job title
52
- about: # Some sleek details about your experience
53
-
54
- contact:
55
- github: # Github username
56
- mobile: # Mobile number
57
- email: # email address
58
- linkedin: # Linkedin username
59
- location:
60
- country: # country name
61
- city: # city name
62
-
63
- skills:
64
- - area: # specific skill area you are specialized in
65
- items:
66
- - # sub item such as a technology you have the skill in under the specialized area
67
-
68
- experiences:
69
- - title: # job title
70
- organisation: # Name of the place you worked
71
- location:
72
- city: # city name
73
- country: # country name
74
- from:
75
- month: # month number that you started
76
- year: # year number that you started
77
- # if following is not given, it is assumed you are still working
78
- to:
79
- month: # month number that you stopped
80
- year: # year number that you stopped
81
- skills:
82
- - # a short name for a specialized skilled you gained
83
- achievements:
84
- - # Things you have achieved or did
85
-
86
- education:
87
- - institute: # name of the place you studied
88
- location:
89
- city: # city name
90
- country: # country name
91
- qualification: # name of the degree/diploma qualification
92
- field: # name of the field such as Computer Science
93
- from:
94
- month: # month number that you started
95
- year: # year number that you started
96
- # if following is not given, it is assumed you are still studying
97
- to:
98
- month: # month number that you stopped
99
- year: # year number that you stopped
100
- achievements:
101
- - # Things you have achieved or did
102
- ```
45
+ First we need to have a `yaml` file or a string with the correct schema data structure for the type of the PDF you are generating.
46
+
47
+ For the list of schemas, check `lib/schema` folder. E.g. For resume it is `lib/schema/resume/schema.yml`.
103
48
 
104
49
  ## Generating PDF
105
50
 
@@ -108,13 +53,14 @@ education:
108
53
  Once you have the YAML text file, feed it into the `bin/console` and specify the output location.
109
54
 
110
55
  ```bash
111
- bin/console -c examples/john_doe.yml -o examples/paper/john_doe.pdf -t paper
56
+ bin/console -c examples/john_doe.yml -o examples/paper/john_doe.pdf -k resume -t paper
112
57
  ```
113
58
 
114
59
  #### Bash options
115
60
 
116
61
  - `-c` the YAML file
117
62
  - `-o` output location for the PDF file including the name
63
+ - `-k` the type of document you want to generate
118
64
  - `-t` template to use
119
65
 
120
66
  ### Using `PDF` class
@@ -125,9 +71,9 @@ yaml_str = "..."
125
71
  # Create a hash
126
72
  content_hash = Kamisaku::Helpers.yaml_str_to_content_hash(yaml_str)
127
73
  # Validate the hash is correct. If there is any issue, it will raise a ` Kamisaku::Error ` exception.
128
- Kamisaku::ContentValidator.new(content_hash:).validate!
74
+ Kamisaku::BaseContentValidator.new(content_hash:).validate!
129
75
  # create a pdf instance
130
- pdf = Kamisaku::PDF.new(content_hash:, template: "paper")
76
+ pdf = Kamisaku::PDF.new(content_hash:, category: "resume", template: "paper")
131
77
  # create the PDF at given path
132
78
  pdf.write_to('/path/to/generated_file.pdf')
133
79
  ```
@@ -20,6 +20,10 @@ module Kamisaku
20
20
  options[:pdf_file] = pdf_file
21
21
  end
22
22
 
23
+ parser.on("-k", "--category CATEGORY", "Provide the TEMPLATE name") do |category|
24
+ options[:category] = category
25
+ end
26
+
23
27
  parser.on("-t", "--template TEMPLATE", "Provide the TEMPLATE name") do |template|
24
28
  options[:template] = template
25
29
  end
@@ -6,7 +6,7 @@ module Kamisaku
6
6
  raise Kamisaku::Error.new "YAML file does not exist" unless File.exist?(yaml_file)
7
7
 
8
8
  yaml_string = File.read(yaml_file)
9
- pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), template: options[:template])
9
+ pdf = PDF.new(content_hash: Helpers.yaml_str_to_content_hash(yaml_string), category: options[:category], template: options[:template])
10
10
  pdf.write_to(options[:pdf_file])
11
11
  pdf.write_to_html_file(options[:html_output]) if options[:html_output]
12
12
  end
@@ -0,0 +1,14 @@
1
+ module Kamisaku
2
+ class BaseContentValidator
3
+ attr_reader :content_hash
4
+ alias_method :data, :content_hash
5
+
6
+ def initialize(content_hash:)
7
+ @content_hash = content_hash
8
+ end
9
+
10
+ def validate!
11
+ # Override by child class
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,218 @@
1
+ module Kamisaku
2
+ class BirthdayInvitationContentValidator < BaseContentValidator
3
+ TEMPLATES = %(
4
+ dino
5
+ ).freeze
6
+
7
+ def validate!
8
+ validate_party_details
9
+ validate_venue
10
+ validate_contact_info
11
+ validate_rsvp
12
+ validate_special_instructions if data[:special_instructions]
13
+ validate_activities if data[:activities]
14
+ end
15
+
16
+ private
17
+
18
+ def validate_party_details
19
+ raise Error, "Missing party_details" unless data[:party_details]
20
+ raise Error, "party_details must be a hash" unless data[:party_details].is_a?(Hash)
21
+
22
+ required_fields = %i[person_name age date start_time]
23
+ allowed_fields = %i[person_name age date start_time end_time theme]
24
+ party_fields = data[:party_details].keys
25
+
26
+ unless party_fields.all? { |field| allowed_fields.include?(field) }
27
+ raise Error, "party_details contains invalid fields"
28
+ end
29
+
30
+ required_fields.each do |field|
31
+ unless party_fields.include?(field)
32
+ raise Error, "party_details missing required field '#{field}'"
33
+ end
34
+ end
35
+
36
+ validate_string_field(data[:party_details][:person_name], "party_details", "person_name")
37
+ validate_integer_field(data[:party_details][:age], "party_details", "age")
38
+ validate_date(data[:party_details][:date], "party_details")
39
+ validate_string_field(data[:party_details][:start_time], "party_details", "start_time")
40
+
41
+ if data[:party_details][:end_time]
42
+ validate_string_field(data[:party_details][:end_time], "party_details", "end_time")
43
+ end
44
+
45
+ if data[:party_details][:theme]
46
+ validate_string_field(data[:party_details][:theme], "party_details", "theme")
47
+ end
48
+ end
49
+
50
+ def validate_venue
51
+ raise Error, "Missing venue" unless data[:venue]
52
+ raise Error, "venue must be a hash" unless data[:venue].is_a?(Hash)
53
+
54
+ required_fields = %i[address]
55
+ allowed_fields = %i[name address additional_instructions]
56
+ venue_fields = data[:venue].keys
57
+
58
+ unless venue_fields.all? { |field| allowed_fields.include?(field) }
59
+ raise Error, "venue contains invalid fields"
60
+ end
61
+
62
+ required_fields.each do |field|
63
+ unless venue_fields.include?(field)
64
+ raise Error, "venue missing required field '#{field}'"
65
+ end
66
+ end
67
+
68
+ validate_string_field(data[:venue][:address], "venue", "address")
69
+
70
+ if data[:venue][:name]
71
+ validate_string_field(data[:venue][:name], "venue", "name")
72
+ end
73
+
74
+ if data[:venue][:additional_instructions]
75
+ validate_string_field(data[:venue][:additional_instructions], "venue", "additional_instructions")
76
+ end
77
+ end
78
+
79
+ def validate_contact_info
80
+ raise Error, "Missing contact_info" unless data[:contact_info]
81
+ raise Error, "contact_info must be a hash" unless data[:contact_info].is_a?(Hash)
82
+
83
+ required_fields = %i[host_name phone]
84
+ allowed_fields = %i[host_name phone email]
85
+ contact_fields = data[:contact_info].keys
86
+
87
+ unless contact_fields.all? { |field| allowed_fields.include?(field) }
88
+ raise Error, "contact_info contains invalid fields"
89
+ end
90
+
91
+ required_fields.each do |field|
92
+ unless contact_fields.include?(field)
93
+ raise Error, "contact_info missing required field '#{field}'"
94
+ end
95
+ end
96
+
97
+ validate_string_field(data[:contact_info][:host_name], "contact_info", "host_name")
98
+ validate_string_field(data[:contact_info][:phone], "contact_info", "phone")
99
+
100
+ if data[:contact_info][:email]
101
+ validate_string_field(data[:contact_info][:email], "contact_info", "email")
102
+ end
103
+ end
104
+
105
+ def validate_rsvp
106
+ raise Error, "Missing rsvp" unless data[:rsvp]
107
+ raise Error, "rsvp must be a hash" unless data[:rsvp].is_a?(Hash)
108
+
109
+ required_fields = %i[deadline method contact]
110
+ rsvp_fields = data[:rsvp].keys
111
+
112
+ unless rsvp_fields.all? { |field| required_fields.include?(field) }
113
+ raise Error, "rsvp contains invalid fields"
114
+ end
115
+
116
+ required_fields.each do |field|
117
+ unless rsvp_fields.include?(field)
118
+ raise Error, "rsvp missing required field '#{field}'"
119
+ end
120
+ end
121
+
122
+ validate_date(data[:rsvp][:deadline], "rsvp")
123
+ validate_string_field(data[:rsvp][:method], "rsvp", "method")
124
+ validate_string_field(data[:rsvp][:contact], "rsvp", "contact")
125
+ end
126
+
127
+ def validate_special_instructions
128
+ raise Error, "special_instructions must be a hash" unless data[:special_instructions].is_a?(Hash)
129
+
130
+ allowed_fields = %i[what_to_bring dress_code food_allergies_note gift_preferences parking_info weather_backup]
131
+ special_fields = data[:special_instructions].keys
132
+
133
+ unless special_fields.all? { |field| allowed_fields.include?(field) }
134
+ raise Error, "special_instructions contains invalid fields"
135
+ end
136
+
137
+ data[:special_instructions].each do |field, value|
138
+ validate_string_field(value, "special_instructions", field.to_s)
139
+ end
140
+ end
141
+
142
+ def validate_activities
143
+ raise Error, "activities must be a hash" unless data[:activities].is_a?(Hash)
144
+
145
+ allowed_fields = %i[main_activities entertainment]
146
+ activities_fields = data[:activities].keys
147
+
148
+ unless activities_fields.all? { |field| allowed_fields.include?(field) }
149
+ raise Error, "activities contains invalid fields"
150
+ end
151
+
152
+ if data[:activities][:main_activities]
153
+ validate_array_of_strings(data[:activities][:main_activities], "activities", "main_activities")
154
+ end
155
+
156
+ if data[:activities][:entertainment]
157
+ validate_string_field(data[:activities][:entertainment], "activities", "entertainment")
158
+ end
159
+ end
160
+
161
+ def validate_date(date, section)
162
+ raise Error, "#{section}: date must be a hash" unless date.is_a?(Hash)
163
+
164
+ required_fields = %i[day month year]
165
+ date_fields = date.keys
166
+
167
+ unless date_fields.all? { |field| required_fields.include?(field) }
168
+ raise Error, "#{section}: date contains invalid fields"
169
+ end
170
+
171
+ required_fields.each do |field|
172
+ unless date_fields.include?(field)
173
+ raise Error, "#{section}: date missing required field '#{field}'"
174
+ end
175
+ validate_integer_field(date[field], section, "date.#{field}")
176
+ end
177
+
178
+ validate_day_range(date[:day], section)
179
+ validate_month_range(date[:month], section)
180
+ end
181
+
182
+ def validate_string_field(value, section, field)
183
+ unless value.is_a?(String)
184
+ raise Error, "#{section}: field '#{field}' must be a string"
185
+ end
186
+ end
187
+
188
+ def validate_integer_field(value, section, field)
189
+ unless value.is_a?(Integer)
190
+ raise Error, "#{section}: field '#{field}' must be an integer"
191
+ end
192
+ end
193
+
194
+ def validate_array_of_strings(value, section, field)
195
+ unless value.is_a?(Array)
196
+ raise Error, "#{section}: field '#{field}' must be an array"
197
+ end
198
+
199
+ value.each do |item|
200
+ unless item.is_a?(String)
201
+ raise Error, "#{section}: each item in '#{field}' must be a string"
202
+ end
203
+ end
204
+ end
205
+
206
+ def validate_day_range(day, section)
207
+ unless day.between?(1, 31)
208
+ raise Error, "#{section}: day must be between 1-31"
209
+ end
210
+ end
211
+
212
+ def validate_month_range(month, section)
213
+ unless month.between?(1, 12)
214
+ raise Error, "#{section}: month must be between 1-12"
215
+ end
216
+ end
217
+ end
218
+ end
@@ -1,11 +1,14 @@
1
1
  module Kamisaku
2
- class ContentValidator
3
- attr_reader :content_hash
4
- alias_method :data, :content_hash
5
-
6
- def initialize(content_hash:)
7
- @content_hash = content_hash
8
- end
2
+ class ResumeContentValidator < Kamisaku::BaseContentValidator
3
+ TEMPLATES = %w[
4
+ paper
5
+ sleek
6
+ zenith
7
+ meridian
8
+ prism
9
+ gradient
10
+ chromatic
11
+ ].freeze
9
12
 
10
13
  def validate!
11
14
  validate_version
@@ -2,13 +2,14 @@ require "erb"
2
2
 
3
3
  module Kamisaku
4
4
  class HtmlBuilder
5
- attr_reader :content_hash, :template
5
+ attr_reader :content_hash, :category, :template
6
6
  alias_method :data, :content_hash
7
7
 
8
8
  include TemplateHelpers
9
9
 
10
- def initialize(content_hash, template)
10
+ def initialize(content_hash, category, template)
11
11
  @content_hash = content_hash
12
+ @category = category
12
13
  @template = template
13
14
  end
14
15
 
@@ -20,7 +21,7 @@ module Kamisaku
20
21
  private
21
22
 
22
23
  def template_html
23
- path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
24
+ path = File.join(File.dirname(__FILE__), "/../templates/#{category}/#{template}/template.html.erb")
24
25
  File.read(path)
25
26
  end
26
27
  end
data/lib/kamisaku/pdf.rb CHANGED
@@ -2,14 +2,22 @@ require "tempfile"
2
2
 
3
3
  module Kamisaku
4
4
  class PDF
5
- attr_reader :content_hash, :template
5
+ attr_reader :content_hash, :category, :template
6
6
 
7
- def initialize(content_hash:, template: nil)
7
+ CONTENT_VALIDATOR_MAP = {
8
+ resume: ResumeContentValidator,
9
+ birthday_invitation: BirthdayInvitationContentValidator
10
+ }
11
+
12
+ def initialize(content_hash:, category:, template:)
8
13
  @content_hash = content_hash
9
- @template = template || "sleek"
10
- ContentValidator.new(content_hash:).validate!
14
+ @category = category
15
+ @template = template
11
16
  raise Error, "Invalid template name '#{template}'" unless template.is_a?(String)
12
- raise Error, "Invalid template name '#{template}'" unless TemplateHelpers::TEMPLATES.include? @template
17
+ validator_klass = CONTENT_VALIDATOR_MAP[category.to_sym]
18
+ raise Error, "Invalid template name '#{category}'" unless validator_klass
19
+ validator_klass.new(content_hash:).validate!
20
+ raise Error, "Invalid template name '#{template}'" unless validator_klass::TEMPLATES.include?(template)
13
21
  end
14
22
 
15
23
  def write_to(pdf_location)
@@ -44,13 +52,8 @@ module Kamisaku
44
52
  def html
45
53
  return @html if defined? @html
46
54
 
47
- builder = HtmlBuilder.new(content_hash, template)
55
+ builder = HtmlBuilder.new(content_hash, category, template)
48
56
  @html = builder.html
49
57
  end
50
-
51
- def template_html
52
- path = File.join(File.dirname(__FILE__), "/../templates/#{template}/template.html.erb")
53
- File.read(path)
54
- end
55
58
  end
56
59
  end
@@ -2,16 +2,6 @@ require "date"
2
2
 
3
3
  module Kamisaku
4
4
  module TemplateHelpers
5
- TEMPLATES = %w[
6
- paper
7
- sleek
8
- zenith
9
- meridian
10
- prism
11
- gradient
12
- chromatic
13
- ].freeze
14
-
15
5
  def month_name(month_int)
16
6
  return "" if month_int.nil?
17
7
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kamisaku
4
- VERSION = "0.3.4"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/kamisaku.rb CHANGED
@@ -2,9 +2,11 @@ require_relative "kamisaku/version"
2
2
  require_relative "kamisaku/errors"
3
3
  require_relative "kamisaku/helpers"
4
4
  require_relative "kamisaku/arg_parser"
5
- require_relative "kamisaku/pdf"
6
5
  require_relative "kamisaku/chrome_runner"
7
6
  require_relative "kamisaku/cli_runner"
8
7
  require_relative "kamisaku/template_helpers"
9
8
  require_relative "kamisaku/html_builder"
10
- require_relative "kamisaku/content_validator"
9
+ require_relative "kamisaku/content_validators/base_content_validator"
10
+ require_relative "kamisaku/content_validators/resume_content_validator"
11
+ require_relative "kamisaku/content_validators/birthday_invitation_content_validator"
12
+ require_relative "kamisaku/pdf"
@@ -0,0 +1,45 @@
1
+ version: 1
2
+ party_details:
3
+ person_name: "Emma"
4
+ age: 6
5
+ date:
6
+ day: 15
7
+ month: 8
8
+ year: 2025
9
+ start_time: "2:00 PM"
10
+ end_time: "5:00 PM"
11
+ theme: "Dinosaur Adventure"
12
+
13
+ venue:
14
+ name: "Dino Discovery Park"
15
+ address: "123 Prehistoric Lane, Jurassic Heights, CA 90210"
16
+ additional_instructions: "Enter through the main gate and follow the dinosaur footprints to the party pavilion"
17
+
18
+ contact_info:
19
+ host_name: "Sarah Johnson"
20
+ phone: "(555) 123-4567"
21
+ email: "sarah.johnson@email.com"
22
+
23
+ rsvp:
24
+ deadline:
25
+ day: 8
26
+ month: 8
27
+ year: 2025
28
+ method: "phone or text"
29
+ contact: "Sarah Johnson"
30
+
31
+ special_instructions:
32
+ what_to_bring: "Comfortable shoes for outdoor activities and a water bottle"
33
+ dress_code: "Dinosaur costumes encouraged! Or wear green and brown colors"
34
+ food_allergies_note: "Please let us know about any food allergies when you RSVP"
35
+ gift_preferences: "Books about dinosaurs or outdoor toys would be wonderful!"
36
+ parking_info: "Free parking available in the north lot"
37
+ weather_backup: "Indoor pavilion available in case of rain"
38
+
39
+ activities:
40
+ main_activities:
41
+ - "Dinosaur fossil dig"
42
+ - "Pin the tail on the T-Rex"
43
+ - "Dinosaur egg hunt"
44
+ - "Prehistoric crafts station"
45
+ entertainment: "Dr. Dino the Paleontologist with live dinosaur show"
@@ -0,0 +1,40 @@
1
+ party_details: # required: Core information about the birthday party
2
+ person_name: # required: Name of the birthday person
3
+ age: # required: Age the birthday person is turning
4
+ date: # required: Date of the party
5
+ day: # required: Day between 1-31
6
+ month: # required: Month between 1-12
7
+ year: # required: year
8
+ start_time: # required: Party start time
9
+ end_time: # optional: Party end time
10
+ theme: # optional: Party theme (e.g., "Princess", "Superhero", "Dinosaurs")
11
+
12
+ venue: # required: Where the party will be held
13
+ name: # optional: Name of the venue (e.g., "Chuck E. Cheese", "Community Center")
14
+ address: # required: Full address of the party location
15
+ additional_instructions: # optional: Parking info, entrance details, etc.
16
+
17
+ contact_info: # required: How to reach the host
18
+ host_name: # required: Name of parent/guardian hosting
19
+ phone: # required: Contact phone number
20
+ email: # optional: Contact email address
21
+
22
+ rsvp: # required: Response information
23
+ deadline: # required: RSVP deadline date
24
+ day: # required: Day between 1-31
25
+ month: # required: Month between 1-12
26
+ year: # required: year
27
+ method: # required: How to RSVP (phone, email, text)
28
+ contact: # required: Who to contact for RSVP
29
+
30
+ special_instructions: # optional: Additional party details
31
+ what_to_bring: # optional: Items guests should bring
32
+ dress_code: # optional: Costume requirements or dress suggestions
33
+ food_allergies_note: # optional: Note about food allergies
34
+ gift_preferences: # optional: Gift suggestions or "no gifts please"
35
+ parking_info: # optional: Parking instructions
36
+ weather_backup: # optional: Indoor backup plan for outdoor parties
37
+
38
+ activities: # optional: Planned party activities
39
+ main_activities: # optional: List of planned activities
40
+ entertainment: # optional: Hired entertainment (clown, magician, etc.)
@@ -0,0 +1,486 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Birthday Party Invitation</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Fredoka+One:wght@400&family=Nunito:wght@400;600;700&display=swap');
9
+
10
+ * {
11
+ margin: 0;
12
+ padding: 0;
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ /* Base print and preview styles */
17
+ @page {
18
+ margin: 0;
19
+ width: 5in;
20
+ height: 7in;
21
+ }
22
+
23
+ @media screen {
24
+ body {
25
+ background: #f8fafc;
26
+ }
27
+ .paper {
28
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
29
+ }
30
+ }
31
+
32
+ @media screen, print {
33
+ .paper {
34
+ background: white;
35
+ margin: 0 auto;
36
+ width: 5in;
37
+ height: 7in;
38
+ }
39
+ }
40
+
41
+ .paper {
42
+ background: linear-gradient(135deg, #C8E6C9 0%, #A5D6A7 50%, #81C784 100%);
43
+ font-family: 'Nunito', sans-serif;
44
+ color: #2E7D32;
45
+ position: relative;
46
+ overflow: hidden;
47
+ }
48
+
49
+ .invitation-container {
50
+ width: 100%;
51
+ height: 100%;
52
+ position: relative;
53
+ background: rgba(255, 255, 255, 0.95);
54
+ border-radius: 20px;
55
+ border: 4px solid #4CAF50;
56
+ box-shadow: 0 8px 16px rgba(0,0,0,0.1);
57
+ padding: 0.3in;
58
+ display: flex;
59
+ flex-direction: column;
60
+ justify-content: space-between;
61
+ }
62
+
63
+ .decoration-top-right {
64
+ position: absolute;
65
+ top: 0.1in;
66
+ right: -0.4in;
67
+ width: 200px;
68
+ height: 200px;
69
+ transform: scalex(-1);
70
+ z-index: 10;
71
+ }
72
+
73
+ /* Decorative dinosaurs */
74
+ .dino-top-left {
75
+ position: absolute;
76
+ top: -0.1in;
77
+ left: -0.4in;
78
+ width: 200px;
79
+ height: auto;
80
+ transform: rotate(-10deg) scalex(-1);
81
+ z-index: 10;
82
+ }
83
+
84
+ .dino-bottom-right {
85
+ position: absolute;
86
+ bottom: -0.8in;
87
+ right: 1.1in;
88
+ width: 55px;
89
+ height: auto;
90
+ transform: rotate(-15deg);
91
+ z-index: 10;
92
+ }
93
+
94
+ /* Balloons */
95
+ .balloon {
96
+ position: absolute;
97
+ font-size: 1.5em;
98
+ animation: float 3s ease-in-out infinite;
99
+ z-index: 5;
100
+ }
101
+
102
+ .balloon-1 { top: 1.2in; left: 0.8in; animation-delay: 0s; color: #E91E63; }
103
+ .balloon-2 { top: 1.8in; right: 0.6in; animation-delay: 1s; color: #2196F3; }
104
+ .balloon-3 { bottom: 2.5in; left: 0.5in; animation-delay: 2s; color: #FF9800; }
105
+ .balloon-4 { top: 2.8in; right: 1in; animation-delay: 1.5s; color: #9C27B0; }
106
+
107
+ /* Tropical leaves */
108
+ .leaf {
109
+ position: absolute;
110
+ font-size: 1.8em;
111
+ color: #2E7D32;
112
+ z-index: 1;
113
+ opacity: 0.7;
114
+ }
115
+
116
+ .leaf-1 { top: 0.5in; left: 1.5in; transform: rotate(-20deg); }
117
+ .leaf-2 { top: 1.5in; right: 1.2in; transform: rotate(45deg); }
118
+ .leaf-3 { bottom: 1in; left: 1in; transform: rotate(-45deg); }
119
+ .leaf-4 { bottom: 2.5in; right: 0.3in; transform: rotate(30deg); }
120
+
121
+ /* Stars decoration */
122
+ .star {
123
+ position: absolute;
124
+ color: #FFD700;
125
+ font-size: 0.8em;
126
+ animation: twinkle 2s infinite;
127
+ z-index: 5;
128
+ }
129
+
130
+ .star-1 { top: 1.2in; left: 2.2in; animation-delay: 0s; }
131
+ .star-2 { top: 3in; left: 3.8in; animation-delay: 0.7s; }
132
+ .star-3 { top: 4.5in; left: 1.8in; animation-delay: 1.4s; }
133
+ .star-4 { top: 5.8in; left: 3.5in; animation-delay: 2.1s; }
134
+
135
+ @keyframes twinkle {
136
+ 0%, 100% { opacity: 1; transform: scale(1); }
137
+ 50% { opacity: 0.5; transform: scale(1.2); }
138
+ }
139
+
140
+ /* Typography */
141
+ .header {
142
+ text-align: center;
143
+ margin-bottom: 0.15in;
144
+ z-index: 20;
145
+ position: relative;
146
+ }
147
+
148
+ .title {
149
+ font-family: 'Fredoka One', cursive;
150
+ font-size: 22px;
151
+ color: #FF6B35;
152
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
153
+ margin-bottom: 5px;
154
+ line-height: 1.1;
155
+ }
156
+
157
+ .child-name {
158
+ font-family: 'Fredoka One', cursive;
159
+ font-size: 26px;
160
+ color: #FF5722;
161
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
162
+ margin-bottom: 3px;
163
+ }
164
+
165
+ .age {
166
+ font-size: 18px;
167
+ color: #E65100;
168
+ font-weight: 700;
169
+ }
170
+
171
+ /* Content sections */
172
+ .content {
173
+ flex: 1;
174
+ display: flex;
175
+ flex-direction: column;
176
+ justify-content: center;
177
+ z-index: 20;
178
+ position: relative;
179
+ margin: 0.1in 0;
180
+ }
181
+
182
+ .info-section {
183
+ background: rgba(255, 255, 255, 0.9);
184
+ border-radius: 12px;
185
+ padding: 10px;
186
+ margin: 6px 0;
187
+ border: 2px solid #4CAF50;
188
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
189
+ }
190
+
191
+ .info-label {
192
+ font-weight: 700;
193
+ color: #2E7D32;
194
+ font-size: 13px;
195
+ display: block;
196
+ margin-bottom: 3px;
197
+ }
198
+
199
+ .info-value {
200
+ color: #1B5E20;
201
+ font-size: 12px;
202
+ line-height: 1.4;
203
+ }
204
+
205
+ .theme-highlight {
206
+ background: linear-gradient(135deg, #FFF3E0 0%, #FFE0B2 100%);
207
+ border: 2px solid #FF8F00;
208
+ text-align: center;
209
+ font-weight: 700;
210
+ color: #E65100;
211
+ }
212
+
213
+ .rsvp-section {
214
+ background: linear-gradient(135deg, #FCE4EC 0%, #F8BBD9 50%);
215
+ border: 2px solid #E91E63;
216
+ text-align: center;
217
+ margin-top: 0.1in;
218
+ z-index: 20;
219
+ }
220
+
221
+ .footer-deco {
222
+ text-align: center;
223
+ font-size: 1.2em;
224
+ margin-top: 8px;
225
+ z-index: 20;
226
+ position: relative;
227
+ color: #4CAF50;
228
+ }
229
+
230
+ /* Print optimizations */
231
+ @media print {
232
+ body {
233
+ margin: 0;
234
+ -webkit-print-color-adjust: exact;
235
+ color-adjust: exact;
236
+ }
237
+ }
238
+ </style>
239
+ </head>
240
+ <body>
241
+ <div class="paper">
242
+ <div class="invitation-container">
243
+ <!-- Decorative dinosaur images -->
244
+ <div class="dino-top-left">
245
+ <?xml version="1.0" encoding="iso-8859-1"?>
246
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
247
+ <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
248
+ viewBox="0 0 382.325 382.325" xml:space="preserve">
249
+ <g id="XMLID_800_">
250
+ <path id="XMLID_801_" style="fill:#DD916F;" d="M182.966,228.544v36h-4c-6.6,0-12,5.4-12,12c0,6.6,5.4,12,12,12h4h24v-24v-36
251
+ H182.966z"/>
252
+ <g id="XMLID_802_">
253
+
254
+ <rect id="XMLID_52_" x="221.674" y="222.703" transform="matrix(0.8321 -0.5547 0.5547 0.8321 -100.9291 172.0605)" style="fill:#DD916F;" width="24" height="60"/>
255
+ <path id="XMLID_51_" style="fill:#DD916F;" d="M246.987,251.039l-23.297,15.532c-5.492,3.661-6.989,11.149-3.328,16.641
256
+ c3.661,5.492,11.149,6.989,16.641,3.328l23.297-15.532L246.987,251.039z"/>
257
+ </g>
258
+ <polygon id="XMLID_803_" style="fill:#D87D60;" points="191.163,228.544 191.163,288.544 206.966,288.544 206.966,264.544
259
+ 206.966,228.544 "/>
260
+ <path id="XMLID_804_" style="fill:#D87D60;" d="M203.078,124.128c-9.166,0-16.59,2.153-22.696,5.62
261
+ c-1.335-5.063-1.692-9.868-1.692-12.65c0-9.402,17.057-23.317,23.007-23.317c5.949,0,23.006,13.915,23.006,23.317
262
+ c0,2.789-0.359,7.609-1.701,12.684C217.433,126.322,211.146,124.128,203.078,124.128z"/>
263
+ <path id="XMLID_805_" style="fill:#D46C5B;" d="M201.697,93.781c-2.354,0-6.447,2.183-10.535,5.398v26.329
264
+ c3.613-0.882,7.562-1.381,11.916-1.381c8.068,0,14.355,2.194,19.924,5.654c1.343-5.075,1.701-9.896,1.701-12.685
265
+ C224.704,107.696,207.647,93.781,201.697,93.781z"/>
266
+ <path id="XMLID_46_" style="fill:#D46C5B;" d="M238.713,136.213c-6.482-6.482-13.253-10.208-20.022-12.075
267
+ c2.636-4.524,5.781-8.174,7.749-10.141c6.648-6.648,28.549-4.426,32.756-0.219c4.207,4.207,6.429,26.107-0.219,32.755
268
+ c-1.972,1.972-5.634,5.127-10.172,7.766C247.311,147.914,244.417,141.917,238.713,136.213z"/>
269
+ <path id="XMLID_45_" style="fill:#D46C5B;" d="M264.213,166.213c-6.482-6.482-13.253-10.208-20.022-12.075
270
+ c2.636-4.524,5.781-8.174,7.749-10.141c6.648-6.648,28.549-4.426,32.756-0.219c4.207,4.207,6.429,26.107-0.219,32.755
271
+ c-1.972,1.972-5.634,5.127-10.172,7.766C272.812,177.914,269.918,171.917,264.213,166.213z"/>
272
+ <path id="XMLID_44_" style="fill:#D46C5B;" d="M290.847,185.83c-3.935-3.936-8.047-6.198-12.157-7.331
273
+ c1.6-2.747,3.51-4.963,4.705-6.158c4.036-4.036,17.334-2.687,19.889-0.133c2.554,2.554,3.903,15.852-0.133,19.888
274
+ c-1.197,1.197-3.421,3.113-6.176,4.715C296.068,192.935,294.311,189.294,290.847,185.83z"/>
275
+ <path id="XMLID_43_" style="fill:#D87D60;" d="M165.12,134.259c-6.482,6.482-10.208,13.253-12.075,20.023
276
+ c-4.524-2.636-8.174-5.781-10.141-7.749c-6.648-6.648-4.426-28.549-0.219-32.756c4.207-4.207,26.107-6.429,32.755,0.219
277
+ c1.972,1.972,5.127,5.634,7.766,10.172C176.822,125.66,170.825,128.554,165.12,134.259z"/>
278
+ <path id="XMLID_42_" style="fill:#D87D60;" d="M134.156,153.693c-5.209,5.209-8.203,10.65-9.703,16.09
279
+ c-3.635-2.118-6.569-4.646-8.15-6.227c-5.342-5.342-3.557-22.941-0.176-26.322c3.381-3.381,20.98-5.166,26.322,0.176
280
+ c1.585,1.585,4.12,4.527,6.241,8.174C143.559,146.783,138.74,149.108,134.156,153.693z"/>
281
+ <g id="XMLID_806_">
282
+ <path id="XMLID_41_" style="fill:#DD916F;" d="M128.159,212.031c-0.909,1.035-8.937,10.623-11.007,28.292
283
+ c-1.939,16.551,1.542,23.371,4.805,26.177c3.538,3.043,8.87,2.642,11.913-0.895c2.664-3.097,0.445-6.75,0.278-10.679
284
+ c-1.15-27.024,15.928-25.551,16.153-25.82l-9.444-5.929L128.159,212.031z"/>
285
+ </g>
286
+ <path id="XMLID_39_" style="fill:#DD916F;" d="M55.199,115.9c1.58,16.485,11.641,2.603,34.471,20s31.966,16.843,39.02,13.882
287
+ c37.37-15.686,43.5-38,88.632-30c44.701,7.924,46.089,37.5,57.017,50c24.166,27.642,84.576,5.667,104.252,40.149
288
+ c12.588,22.061-10.818,62.436-9.223,49.851c5.068-40-4.947-53.314-38.012-50s-57.6,21.657-85.526,33.125
289
+ c-42.784,17.569-93.466-2.431-110.214-16.569c-28.501-24.059-65.46-62.39-80.417-60.733c-14.958,1.657-28.713,5.4-37.438,4.177
290
+ c-7.803-1.094-26.441-8.319-13.058-20.745S53.238,95.449,55.199,115.9z"/>
291
+ <g id="XMLID_807_">
292
+ <path id="XMLID_38_" style="fill:#DD916F;" d="M111.653,189.782c-1.174,0.773-11.663,8.04-18.474,24.867
293
+ c-6.379,15.763-4.795,23.415-2.343,27.057c2.66,3.95,8.015,4.995,11.964,2.337c3.458-2.328,2.262-6.522,3.158-10.434
294
+ c6.168-26.904,22.576-20.841,22.87-21.045l-7.692-8.386L111.653,189.782z"/>
295
+ </g>
296
+ <circle id="XMLID_36_" style="fill:#FFE477;" cx="193.104" cy="162.159" r="10"/>
297
+ <path id="XMLID_808_" style="fill:#D87D60;" d="M378.591,209.931c-19.676-34.482-80.086-12.507-104.252-40.149
298
+ c-10.928-12.5-12.316-42.076-57.017-50c-10.553-1.871-18.973-2.083-26.159-1.111v129.743c17.572,2.602,36.805,1.828,54.668-5.507
299
+ c27.926-11.468,52.462-29.811,85.526-33.125c33.064-3.314,43.08,10,38.012,50C367.773,272.368,391.179,231.992,378.591,209.931z"/>
300
+ <circle id="XMLID_34_" style="fill:#F6E277;" cx="225.999" cy="149.199" r="15"/>
301
+ <circle id="XMLID_33_" style="fill:#F6E277;" cx="248.688" cy="179.78" r="10"/>
302
+ <path id="XMLID_809_" d="M44.69,137.782c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6S47.999,137.782,44.69,137.782z"/>
303
+ <path id="XMLID_810_" style="fill:#F6E277;" d="M202.833,159.846c-1.248-5.249-6.423-8.527-11.671-7.484v19.6
304
+ c1.367,0.272,2.81,0.27,4.255-0.073C200.791,170.611,204.111,165.219,202.833,159.846z"/>
305
+ </g>
306
+ </svg>
307
+ </div>
308
+
309
+ <div class="dino-bottom-right">
310
+ <?xml version="1.0" encoding="iso-8859-1"?>
311
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
312
+ <svg height="200px" width="200px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
313
+ viewBox="0 0 353.123 353.123" xml:space="preserve">
314
+ <g id="XMLID_463_">
315
+ <path id="XMLID_464_" style="fill:#D570A7;" d="M104.581,57.7c4.223,21.21,10.669,46.599,20.944,69.996
316
+ c5.562-2.582,13.012-11.275,10.552-16.799c-1.799-4.038-8.399-5.271-14.023-4.704c5.212-3.253,10.759-9.974,8.939-14.812
317
+ c-1.724-4.585-9.539-6.081-15.514-5.385c5.299-3.049,10.975-9.372,9.418-14.222c-1.451-4.517-8.75-6.42-14.661-6.171
318
+ c5.242-3.091,10.762-9.319,9.223-14.109c-1.451-4.516-8.749-6.42-14.66-6.171c5.242-3.091,10.761-9.318,9.222-14.109
319
+ c-1.768-5.503-12.216-7.126-18.258-5.701C99.738,35.493,102.327,46.38,104.581,57.7z"/>
320
+ <path id="XMLID_42_" style="fill:#DA88B4;" d="M133.291,303.471l-2.898-0.826c-5.201-1.482-8.216-6.9-6.734-12.101l11.585-51.057
321
+ l21.733,6.193l-11.585,51.057C143.91,301.938,138.492,304.953,133.291,303.471z"/>
322
+ <path id="XMLID_41_" style="fill:#DA88B4;" d="M163.564,303.709h-3.013c-5.408,0-9.792-4.384-9.792-9.792l3.205-52.228h22.598
323
+ l-3.205,52.228C173.357,299.324,168.972,303.709,163.564,303.709z"/>
324
+ <path id="XMLID_40_" style="fill:#D570A7;" d="M249.704,303.471l2.898-0.826c5.201-1.482,8.216-6.9,6.734-12.101l-11.585-51.057
325
+ l-21.733,6.193l11.585,51.057C239.085,301.938,244.503,304.953,249.704,303.471z"/>
326
+ <path id="XMLID_39_" style="fill:#D570A7;" d="M219.43,303.709h3.013c5.408,0,9.792-4.384,9.792-9.792l-3.205-52.228h-22.598
327
+ l3.205,52.228C209.638,299.324,214.022,303.709,219.43,303.709z"/>
328
+ <path id="XMLID_466_" style="fill:#DA88B4;" d="M286.793,238.75c-27.199-27.357-30.171-49.76-88.887-79.118
329
+ c-36.069-18.034-64.83-21.45-72.108-41.205c-14.422-39.144-19.687-64.375-23.605-77.485c-1.183-4.131-4.209-33.439-38.202-31.894
330
+ c-17.067,0-30.903,9.224-30.903,20.602s13.836,20.602,30.903,20.602c6.501,0,19.229,5.337,20.602,16.668
331
+ c4.859,40.089,5.666,45.325,19.152,97.392c9.798,37.826,2.137,52.661,30.947,81.517c44.36,44.429,115.945,24.135,122.519,24.135
332
+ c0.98-0.125,43.589,19.794,30.495,49.064c-11.181,24.994-115.189,9.073-134.451,6.551c-53.211-6.967,48.365,39.132,139.027,24.742
333
+ C336.693,343.274,322.526,274.69,286.793,238.75z"/>
334
+ <path id="XMLID_467_" style="fill:#D570A7;" d="M286.793,238.75c-27.199-27.357-30.171-49.76-88.887-79.118
335
+ c-7.473-3.736-14.625-6.84-21.344-9.596v119.823c37.636,10.585,75.926,0.107,80.65,0.107c0.98-0.125,43.589,19.794,30.495,49.064
336
+ c-8.708,19.465-73.712,14.115-111.146,9.604v14.248c30.189,7.904,74.1,14.048,115.721,7.442
337
+ C336.693,343.274,322.526,274.69,286.793,238.75z"/>
338
+ <path id="XMLID_36_" style="fill:#DA88B4;" d="M51.455,25.024l0.147-19.846c0.03-4.009,4.388-6.483,7.845-4.453l17.114,10.05
339
+ L51.455,25.024z"/>
340
+ <path id="XMLID_468_" d="M60.561,15.024c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6S63.87,15.024,60.561,15.024z"/>
341
+ <circle id="XMLID_469_" style="fill:#98C79C;" cx="214.578" cy="217.007" r="8.017"/>
342
+ <circle id="XMLID_470_" style="fill:#98C79C;" cx="223.646" cy="192.108" r="7.085"/>
343
+ <circle id="XMLID_472_" style="fill:#98C79C;" cx="236.393" cy="215.214" r="9.695"/>
344
+ <circle id="XMLID_473_" style="fill:#98C79C;" cx="196.866" cy="194.72" r="9.695"/>
345
+ </g>
346
+ </svg>
347
+ </div>
348
+
349
+ <div class="decoration-top-right">
350
+ <?xml version="1.0" encoding="utf-8"?>
351
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
352
+ <svg viewBox="0 -3.57 537.296 537.296" xmlns="http://www.w3.org/2000/svg">
353
+ <g id="Group_10" data-name="Group 10" transform="translate(-1056.208 1535.36)">
354
+ <path id="path232" d="M1067.7-1116.731a8.07,8.07,0,0,1-10.751-3.987,8.194,8.194,0,0,1,3.965-10.84q142.406-66.26,234.63-165.839,92.092-99.44,134.341-232.293a8.075,8.075,0,0,1,10.173-5.28,8.191,8.191,0,0,1,5.24,10.267q-43.36,136.36-137.89,238.439Q1213-1184.324,1067.7-1116.731" fill="#465da9" fill-rule="evenodd"/>
355
+ <path id="path234" d="M1367.5-1168.97l2.027-36.693a12.961,12.961,0,0,1,11.093-12.227l36.066-5.32a12.766,12.766,0,0,0,10.547-9.12,12.925,12.925,0,0,0-3.654-13.507l-93.665-86.32a12.95,12.95,0,0,0-18.357.827l-60.276,66.467a13.2,13.2,0,0,0,.823,18.507l93.662,86.32a12.669,12.669,0,0,0,13.667,2.467,12.84,12.84,0,0,0,8.067-11.4" fill="#f0393c" fill-rule="evenodd"/>
356
+ <path id="path236" d="M1486.2-1324.437l-5.307-36.346a13,13,0,0,1,8.44-14.227l34.267-12.507a12.815,12.815,0,0,0,8.52-11.053,12.884,12.884,0,0,0-6.267-12.506l-108.933-65.64a12.965,12.965,0,0,0-17.813,4.52l-45.84,77.293a13.179,13.179,0,0,0,4.493,17.96l108.92,65.653a12.672,12.672,0,0,0,13.88-.347,12.882,12.882,0,0,0,5.64-12.8" fill="#f9b317" fill-rule="evenodd"/>
357
+ <path id="path238" d="M1214.952-1049.931,1225-1085.237a12.893,12.893,0,0,1,13.507-9.453l36.336,2.88a12.72,12.72,0,0,0,12.284-6.533,12.919,12.919,0,0,0-.592-13.987l-72.341-105.12a12.964,12.964,0,0,0-18.083-3.293l-73.394,51.333a13.193,13.193,0,0,0-3.269,18.227l72.343,105.12a12.7,12.7,0,0,0,12.792,5.453,12.766,12.766,0,0,0,10.372-9.32" fill="#43bb4e" fill-rule="evenodd"/>
358
+ <path id="path240" d="M1132.248-1005.211a17.976,17.976,0,0,0,17.867-18.013,17.976,17.976,0,0,0-17.867-18.013,17.977,17.977,0,0,0-17.868,18.013,17.977,17.977,0,0,0,17.868,18.013" fill="#f9b317" fill-rule="evenodd"/>
359
+ <path id="path242" d="M1346.637-1465.236a11.506,11.506,0,0,0,11.44-11.52,11.518,11.518,0,0,0-11.44-11.533,11.506,11.506,0,0,0-11.427,11.533,11.5,11.5,0,0,0,11.427,11.52" fill="#43bb4e" fill-rule="evenodd"/>
360
+ <path id="path244" d="M1368.8-1100.571a4.47,4.47,0,0,1,3.6-5.16,4.419,4.419,0,0,1,5.12,3.627l3.333,19.333a4.45,4.45,0,0,1-3.6,5.16,4.439,4.439,0,0,1-5.12-3.64l-3.333-19.32" fill="#5b7fc9" fill-rule="evenodd"/>
361
+ <path id="path246" d="M1375.17-1063.651a4.456,4.456,0,0,1,3.6-5.173,4.453,4.453,0,0,1,5.12,3.64l3.333,19.32a4.463,4.463,0,0,1-3.6,5.16,4.419,4.419,0,0,1-5.12-3.627l-3.333-19.32" fill="#5b7fc9" fill-rule="evenodd"/>
362
+ <path id="path248" d="M1350.877-1063.931a4.42,4.42,0,0,1-5.12-3.627,4.443,4.443,0,0,1,3.6-5.16l19.173-3.36a4.433,4.433,0,0,1,5.12,3.627,4.469,4.469,0,0,1-3.613,5.16l-19.16,3.36" fill="#5b7fc9" fill-rule="evenodd"/>
363
+ <path id="path250" d="M1387.49-1070.344a4.438,4.438,0,0,1-5.12-3.64,4.47,4.47,0,0,1,3.6-5.16l19.173-3.36a4.426,4.426,0,0,1,5.12,3.627,4.476,4.476,0,0,1-3.6,5.173l-19.173,3.36" fill="#5b7fc9" fill-rule="evenodd"/>
364
+ <path id="path252" d="M1228.576-1375.517a50.6,50.6,0,0,1,27.487-29.36,3.669,3.669,0,0,0,2.247-3.533,3.649,3.649,0,0,0-2.447-3.4,50.559,50.559,0,0,1-29.125-27.707,3.609,3.609,0,0,0-3.511-2.267,3.626,3.626,0,0,0-3.373,2.467,50.6,50.6,0,0,1-27.488,29.36,3.665,3.665,0,0,0-2.244,3.533,3.671,3.671,0,0,0,2.445,3.4,50.5,50.5,0,0,1,29.125,27.706,3.646,3.646,0,0,0,3.511,2.267,3.626,3.626,0,0,0,3.373-2.467" fill="#5b7fc9" fill-rule="evenodd"/>
365
+ <path id="path254" d="M1238.449-1422.623q-1.824,25.84-28.471,30.413a50.582,50.582,0,0,1,11.713,16.893,3.646,3.646,0,0,0,3.511,2.267,3.626,3.626,0,0,0,3.373-2.467,50.6,50.6,0,0,1,27.487-29.36,3.669,3.669,0,0,0,2.247-3.533,3.649,3.649,0,0,0-2.447-3.4,50.248,50.248,0,0,1-17.413-10.813" fill="#4267ba" fill-rule="evenodd"/>
366
+ <path id="path256" d="M1563.769-1449.77a50.566,50.566,0,0,1,27.48-29.36,3.681,3.681,0,0,0,2.253-3.533,3.685,3.685,0,0,0-2.453-3.413,50.468,50.468,0,0,1-29.12-27.707,3.674,3.674,0,0,0-3.52-2.267,3.674,3.674,0,0,0-3.373,2.467,50.549,50.549,0,0,1-27.48,29.373,3.681,3.681,0,0,0-2.253,3.533,3.682,3.682,0,0,0,2.453,3.4,50.51,50.51,0,0,1,29.12,27.706,3.636,3.636,0,0,0,3.52,2.267,3.637,3.637,0,0,0,3.373-2.467" fill="#6759b2" fill-rule="evenodd"/>
367
+ <path id="path258" d="M1573.636-1496.876q-1.82,25.84-28.467,30.413a50.392,50.392,0,0,1,11.707,16.893,3.636,3.636,0,0,0,3.52,2.267,3.637,3.637,0,0,0,3.373-2.467,50.566,50.566,0,0,1,27.48-29.36,3.681,3.681,0,0,0,2.253-3.533,3.685,3.685,0,0,0-2.453-3.413,50.141,50.141,0,0,1-17.413-10.8" fill="#5748a7" fill-rule="evenodd"/>
368
+ <path id="path260" d="M1126.5-1278.943l-4.051,29.8a4.987,4.987,0,0,1-5.6,4.28l-29.56-4.08a5.023,5.023,0,0,1-4.249-5.653l4.049-29.787a5,5,0,0,1,5.6-4.293l29.56,4.093a5.011,5.011,0,0,1,4.249,5.64" fill="#f0393c" fill-rule="evenodd"/>
369
+ <path id="path262" d="M1124.473-1264.05q-19.546,10.44-39.4-5.44l-2.028,14.893a5.022,5.022,0,0,0,4.249,5.653l29.559,4.08a4.984,4.984,0,0,0,5.6-4.28l2.025-14.907" fill="#cb3325" fill-rule="evenodd"/>
370
+ <path id="path264" d="M1534.183-1257.1l21.093,21.267a5.046,5.046,0,0,1,0,7.08l-21.093,21.266a4.957,4.957,0,0,1-7.027,0l-21.107-21.266a5.046,5.046,0,0,1,0-7.08l21.107-21.267a4.934,4.934,0,0,1,7.027,0" fill="#43bb4e" fill-rule="evenodd"/>
371
+ <path id="path266" d="M1544.73-1246.477q-3.58,22-28.12,28.36l10.547,10.627a4.957,4.957,0,0,0,7.027,0l21.093-21.267a5.046,5.046,0,0,0,0-7.08l-10.547-10.64" fill="#32a940" fill-rule="evenodd"/>
372
+ </g>
373
+ </svg>
374
+ </div>
375
+
376
+ <!-- Balloons -->
377
+ <div class="balloon balloon-1">🎈</div>
378
+ <div class="balloon balloon-2">🎈</div>
379
+ <div class="balloon balloon-3">🎈</div>
380
+ <div class="balloon balloon-4">🎈</div>
381
+
382
+ <!-- Tropical leaves -->
383
+ <div class="leaf leaf-1">🌿</div>
384
+ <div class="leaf leaf-2">🍃</div>
385
+ <div class="leaf leaf-3">🌿</div>
386
+ <div class="leaf leaf-4">🍃</div>
387
+
388
+ <!-- Stars -->
389
+ <div class="star star-1">⭐</div>
390
+ <div class="star star-2">✨</div>
391
+ <div class="star star-3">⭐</div>
392
+ <div class="star star-4">✨</div>
393
+
394
+ <!-- Header -->
395
+ <div class="header">
396
+ <div class="title">🎉 JOIN US FOR A BIRTHDAY PARTY 🎉</div>
397
+ <div class="child-name"><%= data[:party_details][:person_name] %> is turning <%= data[:party_details][:age] %>!</div>
398
+ </div>
399
+
400
+ <!-- Content -->
401
+ <div class="content">
402
+ <% if data[:party_details][:theme] %>
403
+ <div class="info-section theme-highlight">
404
+ <span class="info-label">🎭 PARTY THEME</span>
405
+ <div class="info-value"><%= data[:party_details][:theme] %></div>
406
+ </div>
407
+ <% end %>
408
+
409
+ <div style="display: flex">
410
+ <div class="info-section">
411
+ <span class="info-label">📅 WHEN</span>
412
+ <div class="info-value">
413
+ <%= Date.new(data[:party_details][:date][:year], data[:party_details][:date][:month], data[:party_details][:date][:day]).strftime("%A, %B %d, %Y") %>
414
+ <br>
415
+ <%= data[:party_details][:start_time] %><% if data[:party_details][:end_time] %> - <%= data[:party_details][:end_time] %><% end %>
416
+ </div>
417
+ </div>
418
+
419
+ <div class="info-section">
420
+ <span class="info-label">📍 WHERE</span>
421
+ <div class="info-value">
422
+ <% if data[:venue][:name] %>
423
+ <strong><%= data[:venue][:name] %></strong><br>
424
+ <% end %>
425
+ <%= data[:venue][:address] %>
426
+ <% if data[:venue][:additional_instructions] %>
427
+ <br><em><%= data[:venue][:additional_instructions] %></em>
428
+ <% end %>
429
+ </div>
430
+ </div>
431
+ </div>
432
+
433
+ <% if data[:activities] && (data[:activities][:main_activities] || data[:activities][:entertainment]) %>
434
+ <div class="info-section">
435
+ <span class="info-label">🎮 FUN ACTIVITIES</span>
436
+ <div class="info-value">
437
+ <% if data[:activities][:main_activities] %>
438
+ <%= [data[:activities][:main_activities]].flatten.join(" • ") %>
439
+ <% end %>
440
+ <% if data[:activities][:entertainment] %>
441
+ <% if data[:activities][:main_activities] %><br><% end %>
442
+ <strong>Special Guest:</strong> <%= data[:activities][:entertainment] %>
443
+ <% end %>
444
+ </div>
445
+ </div>
446
+ <% end %>
447
+
448
+ <div style="display: flex">
449
+ <% if data[:special_instructions] %>
450
+ <% if data[:special_instructions][:dress_code] %>
451
+ <div class="info-section">
452
+ <span class="info-label">👕 DRESS CODE</span>
453
+ <div class="info-value"><%= data[:special_instructions][:dress_code] %></div>
454
+ </div>
455
+ <% end %>
456
+
457
+ <% if data[:special_instructions][:what_to_bring] %>
458
+ <div class="info-section">
459
+ <span class="info-label">🎒 PLEASE BRING</span>
460
+ <div class="info-value"><%= data[:special_instructions][:what_to_bring] %></div>
461
+ </div>
462
+ <% end %>
463
+ <% end %>
464
+ </div>
465
+ </div>
466
+
467
+ <!-- RSVP Section -->
468
+ <div class="info-section rsvp-section">
469
+ <span class="info-label">💌 RSVP BY <%= Date.new(data[:rsvp][:deadline][:year], data[:rsvp][:deadline][:month], data[:rsvp][:deadline][:day]).strftime("%B %d").upcase %></span>
470
+ <div class="info-value">
471
+ <strong><%= data[:contact_info][:host_name] %></strong><br>
472
+ <% if data[:rsvp][:method].downcase.include?('phone') || data[:rsvp][:method].downcase.include?('text') %>
473
+ 📞 <%= data[:contact_info][:phone] %>
474
+ <% end %>
475
+ <% if data[:rsvp][:method].downcase.include?('email') && data[:contact_info][:email] %>
476
+ <% if data[:rsvp][:method].downcase.include?('phone') || data[:rsvp][:method].downcase.include?('text') %><br><% end %>
477
+ 📧 <%= data[:contact_info][:email] %>
478
+ <% end %>
479
+ </div>
480
+ </div>
481
+
482
+ <div class="footer-deco">🌟 Let's have a ROARING good time! 🌟</div>
483
+ </div>
484
+ </div>
485
+ </body>
486
+ </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamisaku
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sinaru Gunawardena
@@ -25,35 +25,41 @@ files:
25
25
  - LICENSE.txt
26
26
  - README.md
27
27
  - Rakefile
28
- - examples/chromatic/example.pdf
29
- - examples/gradient/example.pdf
30
- - examples/meridian/example.pdf
31
- - examples/paper/example.pdf
32
- - examples/prism/example.pdf
33
- - examples/sleek/example.pdf
34
- - examples/zenith/example.pdf
28
+ - examples/birthday_invitation/dino/invitation.pdf
29
+ - examples/resume/chromatic/example.pdf
30
+ - examples/resume/gradient/example.pdf
31
+ - examples/resume/meridian/example.pdf
32
+ - examples/resume/paper/example.pdf
33
+ - examples/resume/prism/example.pdf
34
+ - examples/resume/sleek/example.pdf
35
+ - examples/resume/zenith/example.pdf
35
36
  - kamisaku.gemspec
36
37
  - kamisaku.png
37
38
  - lib/kamisaku.rb
38
39
  - lib/kamisaku/arg_parser.rb
39
40
  - lib/kamisaku/chrome_runner.rb
40
41
  - lib/kamisaku/cli_runner.rb
41
- - lib/kamisaku/content_validator.rb
42
+ - lib/kamisaku/content_validators/base_content_validator.rb
43
+ - lib/kamisaku/content_validators/birthday_invitation_content_validator.rb
44
+ - lib/kamisaku/content_validators/resume_content_validator.rb
42
45
  - lib/kamisaku/errors.rb
43
46
  - lib/kamisaku/helpers.rb
44
47
  - lib/kamisaku/html_builder.rb
45
48
  - lib/kamisaku/pdf.rb
46
49
  - lib/kamisaku/template_helpers.rb
47
50
  - lib/kamisaku/version.rb
48
- - lib/schema/example.yml
49
- - lib/schema/schema.yml
50
- - lib/templates/chromatic/template.html.erb
51
- - lib/templates/gradient/template.html.erb
52
- - lib/templates/meridian/template.html.erb
53
- - lib/templates/paper/template.html.erb
54
- - lib/templates/prism/template.html.erb
55
- - lib/templates/sleek/template.html.erb
56
- - lib/templates/zenith/template.html.erb
51
+ - lib/schema/birthday_invitation/example.yml
52
+ - lib/schema/birthday_invitation/schema.yml
53
+ - lib/schema/resume/example.yml
54
+ - lib/schema/resume/schema.yml
55
+ - lib/templates/birthday_invitation/dino/template.html.erb
56
+ - lib/templates/resume/chromatic/template.html.erb
57
+ - lib/templates/resume/gradient/template.html.erb
58
+ - lib/templates/resume/meridian/template.html.erb
59
+ - lib/templates/resume/paper/template.html.erb
60
+ - lib/templates/resume/prism/template.html.erb
61
+ - lib/templates/resume/sleek/template.html.erb
62
+ - lib/templates/resume/zenith/template.html.erb
57
63
  - scripts/rebuild_examples.rb
58
64
  - sig/kamisaku.rbs
59
65
  homepage: https://github.com/sinaru/kamisaku
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes