tina4ruby 0.5.1 → 3.0.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -1
  3. data/README.md +360 -559
  4. data/exe/{tina4 → tina4ruby} +1 -0
  5. data/lib/tina4/ai.rb +312 -0
  6. data/lib/tina4/auth.rb +44 -3
  7. data/lib/tina4/auto_crud.rb +163 -0
  8. data/lib/tina4/cli.rb +242 -77
  9. data/lib/tina4/constants.rb +46 -0
  10. data/lib/tina4/container.rb +54 -0
  11. data/lib/tina4/cors.rb +74 -0
  12. data/lib/tina4/database/sqlite3_adapter.rb +139 -0
  13. data/lib/tina4/database.rb +43 -7
  14. data/lib/tina4/debug.rb +4 -79
  15. data/lib/tina4/dev_admin.rb +1162 -0
  16. data/lib/tina4/dev_mailbox.rb +191 -0
  17. data/lib/tina4/dev_reload.rb +9 -9
  18. data/lib/tina4/drivers/firebird_driver.rb +19 -3
  19. data/lib/tina4/drivers/mssql_driver.rb +3 -3
  20. data/lib/tina4/drivers/mysql_driver.rb +4 -4
  21. data/lib/tina4/drivers/postgres_driver.rb +9 -2
  22. data/lib/tina4/drivers/sqlite_driver.rb +1 -1
  23. data/lib/tina4/env.rb +42 -2
  24. data/lib/tina4/error_overlay.rb +252 -0
  25. data/lib/tina4/events.rb +90 -0
  26. data/lib/tina4/field_types.rb +4 -0
  27. data/lib/tina4/frond.rb +1336 -0
  28. data/lib/tina4/gallery/auth/meta.json +1 -0
  29. data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -0
  30. data/lib/tina4/gallery/database/meta.json +1 -0
  31. data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -0
  32. data/lib/tina4/gallery/error-overlay/meta.json +1 -0
  33. data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -0
  34. data/lib/tina4/gallery/orm/meta.json +1 -0
  35. data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -0
  36. data/lib/tina4/gallery/queue/meta.json +1 -0
  37. data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +27 -0
  38. data/lib/tina4/gallery/rest-api/meta.json +1 -0
  39. data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -0
  40. data/lib/tina4/gallery/templates/meta.json +1 -0
  41. data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -0
  42. data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -0
  43. data/lib/tina4/health.rb +39 -0
  44. data/lib/tina4/html_element.rb +148 -0
  45. data/lib/tina4/localization.rb +2 -2
  46. data/lib/tina4/log.rb +203 -0
  47. data/lib/tina4/messenger.rb +484 -0
  48. data/lib/tina4/migration.rb +132 -29
  49. data/lib/tina4/orm.rb +337 -31
  50. data/lib/tina4/public/css/tina4.css +178 -1
  51. data/lib/tina4/public/css/tina4.min.css +1 -2
  52. data/lib/tina4/public/favicon.ico +0 -0
  53. data/lib/tina4/public/images/logo.svg +5 -0
  54. data/lib/tina4/public/images/tina4-logo-icon.webp +0 -0
  55. data/lib/tina4/public/js/frond.min.js +420 -0
  56. data/lib/tina4/public/js/tina4-dev-admin.min.js +367 -0
  57. data/lib/tina4/public/js/tina4.min.js +93 -0
  58. data/lib/tina4/public/swagger/index.html +90 -0
  59. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -0
  60. data/lib/tina4/queue.rb +40 -4
  61. data/lib/tina4/queue_backends/lite_backend.rb +88 -0
  62. data/lib/tina4/rack_app.rb +314 -23
  63. data/lib/tina4/rate_limiter.rb +123 -0
  64. data/lib/tina4/request.rb +61 -15
  65. data/lib/tina4/response.rb +54 -24
  66. data/lib/tina4/response_cache.rb +134 -0
  67. data/lib/tina4/router.rb +90 -15
  68. data/lib/tina4/scss_compiler.rb +2 -2
  69. data/lib/tina4/seeder.rb +56 -61
  70. data/lib/tina4/service_runner.rb +303 -0
  71. data/lib/tina4/session.rb +85 -0
  72. data/lib/tina4/session_handlers/mongo_handler.rb +1 -1
  73. data/lib/tina4/session_handlers/valkey_handler.rb +43 -0
  74. data/lib/tina4/shutdown.rb +84 -0
  75. data/lib/tina4/sql_translation.rb +295 -0
  76. data/lib/tina4/template.rb +36 -6
  77. data/lib/tina4/templates/base.twig +2 -2
  78. data/lib/tina4/templates/errors/302.twig +14 -0
  79. data/lib/tina4/templates/errors/401.twig +9 -0
  80. data/lib/tina4/templates/errors/403.twig +22 -15
  81. data/lib/tina4/templates/errors/404.twig +22 -15
  82. data/lib/tina4/templates/errors/500.twig +31 -15
  83. data/lib/tina4/templates/errors/502.twig +9 -0
  84. data/lib/tina4/templates/errors/503.twig +12 -0
  85. data/lib/tina4/templates/errors/base.twig +37 -0
  86. data/lib/tina4/version.rb +1 -1
  87. data/lib/tina4/webserver.rb +28 -18
  88. data/lib/tina4.rb +67 -21
  89. metadata +52 -19
  90. data/lib/tina4/public/js/tina4.js +0 -134
  91. data/lib/tina4/public/js/tina4helper.js +0 -387
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Tina4
4
4
  class WebServer
5
- def initialize(app, host: "0.0.0.0", port: 7145)
5
+ def initialize(app, host: "0.0.0.0", port: 7147)
6
6
  @app = app
7
7
  @host = host
8
8
  @port = port
@@ -11,8 +11,8 @@ module Tina4
11
11
  def start
12
12
  require "webrick"
13
13
  require "stringio"
14
- Tina4.print_banner
15
- Tina4::Debug.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
14
+ Tina4.print_banner(host: @host, port: @port)
15
+ Tina4::Log.info("Starting Tina4 WEBrick server on http://#{@host}:#{@port}")
16
16
  @server = WEBrick::HTTPServer.new(
17
17
  BindAddress: @host,
18
18
  Port: @port,
@@ -20,6 +20,9 @@ module Tina4
20
20
  AccessLog: []
21
21
  )
22
22
 
23
+ # Setup graceful shutdown with WEBrick server reference
24
+ Tina4::Shutdown.setup(server: @server)
25
+
23
26
  # Use a custom servlet that passes ALL methods (including OPTIONS) to Rack
24
27
  rack_app = @app
25
28
  servlet = Class.new(WEBrick::HTTPServlet::AbstractServlet) do
@@ -35,21 +38,31 @@ module Tina4
35
38
  end
36
39
 
37
40
  define_method(:handle_request) do |webrick_req, webrick_res|
38
- env = build_rack_env(webrick_req)
39
- status, headers, body = @app.call(env)
41
+ # Reject new requests during shutdown
42
+ if Tina4::Shutdown.shutting_down?
43
+ webrick_res.status = 503
44
+ webrick_res.body = '{"error":"Service shutting down"}'
45
+ webrick_res["content-type"] = "application/json"
46
+ return
47
+ end
48
+
49
+ Tina4::Shutdown.track_request do
50
+ env = build_rack_env(webrick_req)
51
+ status, headers, body = @app.call(env)
40
52
 
41
- webrick_res.status = status
42
- headers.each do |key, value|
43
- if key.downcase == "set-cookie"
44
- Array(value.split("\n")).each { |c| webrick_res.cookies << WEBrick::Cookie.parse_set_cookie(c) }
45
- else
46
- webrick_res[key] = value
53
+ webrick_res.status = status
54
+ headers.each do |key, value|
55
+ if key.downcase == "set-cookie"
56
+ Array(value.split("\n")).each { |c| webrick_res.cookies << WEBrick::Cookie.parse_set_cookie(c) }
57
+ else
58
+ webrick_res[key] = value
59
+ end
47
60
  end
48
- end
49
61
 
50
- response_body = ""
51
- body.each { |chunk| response_body += chunk }
52
- webrick_res.body = response_body
62
+ response_body = ""
63
+ body.each { |chunk| response_body += chunk }
64
+ webrick_res.body = response_body
65
+ end
53
66
  end
54
67
 
55
68
  define_method(:build_rack_env) do |req|
@@ -88,9 +101,6 @@ module Tina4
88
101
  servlet.define_method(:webrick_req_port) { port }
89
102
 
90
103
  @server.mount("/", servlet, rack_app)
91
-
92
- trap("INT") { @server.shutdown }
93
- trap("TERM") { @server.shutdown }
94
104
  @server.start
95
105
  end
96
106
 
data/lib/tina4.rb CHANGED
@@ -10,7 +10,9 @@ end
10
10
 
11
11
  # ── Core (always loaded) ──────────────────────────────────────────────
12
12
  require_relative "tina4/version"
13
- require_relative "tina4/debug"
13
+ require_relative "tina4/constants"
14
+ require_relative "tina4/log"
15
+ require_relative "tina4/debug" # backward compat alias
14
16
  require_relative "tina4/env"
15
17
  require_relative "tina4/router"
16
18
  require_relative "tina4/request"
@@ -21,12 +23,30 @@ require_relative "tina4/database_result"
21
23
  require_relative "tina4/field_types"
22
24
  require_relative "tina4/orm"
23
25
  require_relative "tina4/migration"
26
+ require_relative "tina4/auto_crud"
27
+ require_relative "tina4/database/sqlite3_adapter"
24
28
  require_relative "tina4/template"
29
+ require_relative "tina4/frond"
25
30
  require_relative "tina4/auth"
26
31
  require_relative "tina4/session"
27
32
  require_relative "tina4/middleware"
33
+ require_relative "tina4/cors"
34
+ require_relative "tina4/rate_limiter"
35
+ require_relative "tina4/health"
36
+ require_relative "tina4/shutdown"
28
37
  require_relative "tina4/localization"
38
+ require_relative "tina4/container"
29
39
  require_relative "tina4/queue"
40
+ require_relative "tina4/service_runner"
41
+ require_relative "tina4/events"
42
+ require_relative "tina4/dev_admin"
43
+ require_relative "tina4/messenger"
44
+ require_relative "tina4/dev_mailbox"
45
+ require_relative "tina4/ai"
46
+ require_relative "tina4/sql_translation"
47
+ require_relative "tina4/response_cache"
48
+ require_relative "tina4/html_element"
49
+ require_relative "tina4/error_overlay"
30
50
 
31
51
  module Tina4
32
52
  # ── Lazy-loaded: database drivers ─────────────────────────────────────
@@ -42,7 +62,8 @@ module Tina4
42
62
  module SessionHandlers
43
63
  autoload :FileHandler, File.expand_path("tina4/session_handlers/file_handler", __dir__)
44
64
  autoload :RedisHandler, File.expand_path("tina4/session_handlers/redis_handler", __dir__)
45
- autoload :MongoHandler, File.expand_path("tina4/session_handlers/mongo_handler", __dir__)
65
+ autoload :MongoHandler, File.expand_path("tina4/session_handlers/mongo_handler", __dir__)
66
+ autoload :ValkeyHandler, File.expand_path("tina4/session_handlers/valkey_handler", __dir__)
46
67
  end
47
68
 
48
69
  # ── Lazy-loaded: queue backends ───────────────────────────────────────
@@ -72,27 +93,38 @@ module Tina4
72
93
  autoload :ScssCompiler, File.expand_path("tina4/scss_compiler", __dir__)
73
94
  autoload :DevReload, File.expand_path("tina4/dev_reload", __dir__)
74
95
  autoload :FakeData, File.expand_path("tina4/seeder", __dir__)
75
- autoload :Seeder, File.expand_path("tina4/seeder", __dir__)
76
96
  autoload :WSDL, File.expand_path("tina4/wsdl", __dir__)
77
97
  BANNER = <<~'BANNER'
78
98
 
79
- ████████╗██╗███╗ ██╗ █████╗ ██╗ ██╗
80
- ╚══██╔══╝██║████╗ ██║██╔══██╗██║ ██║
81
- ██║ ██║██╔██╗ ██║███████║███████║
82
- ██║ ██║██║╚██╗██║██╔══██║╚════██║
83
- ██║ ██║██║ ╚████║██║ ██║ ██║
84
- ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝
99
+ ______ _ __ __
100
+ /_ __/(_)___ ____ _/ // /
101
+ / / / / __ \/ __ `/ // /_
102
+ / / / / / / / /_/ /__ __/
103
+ /_/ /_/_/ /_/\__,_/ /_/
85
104
  BANNER
86
105
 
87
106
  class << self
88
107
  attr_accessor :root_dir, :database
89
108
 
90
- def print_banner
91
- puts "\e[35m#{BANNER}\e[0m"
92
- puts " \e[35mTina4 Ruby v#{VERSION} - This is not a framework\e[0m"
109
+ def print_banner(host: "0.0.0.0", port: 7147)
110
+ is_tty = $stdout.respond_to?(:isatty) && $stdout.isatty
111
+ color = is_tty ? "\e[31m" : ""
112
+ reset = is_tty ? "\e[0m" : ""
113
+
114
+ is_debug = Tina4::Env.truthy?(ENV["TINA4_DEBUG"])
115
+ log_level = (ENV["TINA4_LOG_LEVEL"] || "[TINA4_LOG_ALL]").upcase
116
+ display = (host == "0.0.0.0" || host == "::") ? "localhost" : host
117
+
118
+ puts "#{color}#{BANNER}#{reset}"
119
+ puts " Tina4 Ruby v#{VERSION} — This is not a framework"
120
+ puts ""
121
+ puts " Server: http://#{display}:#{port}"
122
+ puts " Swagger: http://localhost:#{port}/swagger"
123
+ puts " Dashboard: http://localhost:#{port}/__dev"
124
+ puts " Debug: #{is_debug ? 'ON' : 'OFF'} (Log level: #{log_level})"
93
125
  puts ""
94
126
  rescue
95
- puts "\e[35mTINA4 Ruby v#{VERSION}\e[0m"
127
+ puts "#{color}TINA4 Ruby v#{VERSION}#{reset}"
96
128
  end
97
129
 
98
130
  def initialize!(root_dir = Dir.pwd)
@@ -102,11 +134,11 @@ module Tina4
102
134
  print_banner
103
135
 
104
136
  # Load environment
105
- Tina4::Env.load(root_dir)
137
+ Tina4::Env.load_env(root_dir)
106
138
 
107
139
  # Setup debug logging
108
- Tina4::Debug.setup(root_dir)
109
- Tina4::Debug.info("Tina4 Ruby v#{VERSION} initializing...")
140
+ Tina4::Log.setup(root_dir)
141
+ Tina4::Log.info("Tina4 Ruby v#{VERSION} initializing...")
110
142
 
111
143
  # Setup auth keys
112
144
  Tina4::Auth.setup(root_dir)
@@ -120,7 +152,7 @@ module Tina4
120
152
  # Auto-discover routes
121
153
  auto_discover(root_dir)
122
154
 
123
- Tina4::Debug.info("Tina4 initialized successfully")
155
+ Tina4::Log.info("Tina4 initialized successfully")
124
156
  end
125
157
 
126
158
  # DSL methods for route registration
@@ -217,6 +249,20 @@ module Tina4
217
249
  Tina4::Localization.t(key, **options)
218
250
  end
219
251
 
252
+ # Service runner DSL
253
+ def service(name, options = {}, &block)
254
+ Tina4::ServiceRunner.register(name, nil, options, &block)
255
+ end
256
+
257
+ # DI container shortcuts
258
+ def register(name, instance = nil, &block)
259
+ Tina4::Container.register(name, instance, &block)
260
+ end
261
+
262
+ def resolve(name)
263
+ Tina4::Container.resolve(name)
264
+ end
265
+
220
266
  private
221
267
 
222
268
  # Resolve auth option for route registration
@@ -240,9 +286,9 @@ module Tina4
240
286
  if db_url && !db_url.empty?
241
287
  begin
242
288
  @database = Tina4::Database.new(db_url)
243
- Tina4::Debug.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
289
+ Tina4::Log.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
244
290
  rescue => e
245
- Tina4::Debug.error("Database connection failed: #{e.message}")
291
+ Tina4::Log.error("Database connection failed: #{e.message}")
246
292
  end
247
293
  end
248
294
  end
@@ -256,9 +302,9 @@ module Tina4
256
302
  Dir.glob(File.join(full_dir, "**/*.rb")).sort.each do |file|
257
303
  begin
258
304
  load file
259
- Tina4::Debug.debug("Auto-loaded: #{file}")
305
+ Tina4::Log.debug("Auto-loaded: #{file}")
260
306
  rescue => e
261
- Tina4::Debug.error("Failed to load #{file}: #{e.message}")
307
+ Tina4::Log.error("Failed to load #{file}: #{e.message}")
262
308
  end
263
309
  end
264
310
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
@@ -93,20 +93,6 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '3.1'
96
- - !ruby/object:Gem::Dependency
97
- name: thor
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - "~>"
101
- - !ruby/object:Gem::Version
102
- version: '1.3'
103
- type: :runtime
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - "~>"
108
- - !ruby/object:Gem::Version
109
- version: '1.3'
110
96
  - !ruby/object:Gem::Dependency
111
97
  name: json
112
98
  requirement: !ruby/object:Gem::Requirement
@@ -237,23 +223,31 @@ description: A lightweight, zero-configuration, Windows-friendly Ruby web framew
237
223
  email:
238
224
  - info@tina4.com
239
225
  executables:
240
- - tina4
226
+ - tina4ruby
241
227
  extensions: []
242
228
  extra_rdoc_files: []
243
229
  files:
244
230
  - CHANGELOG.md
245
231
  - LICENSE.txt
246
232
  - README.md
247
- - exe/tina4
233
+ - exe/tina4ruby
248
234
  - lib/tina4.rb
235
+ - lib/tina4/ai.rb
249
236
  - lib/tina4/api.rb
250
237
  - lib/tina4/auth.rb
238
+ - lib/tina4/auto_crud.rb
251
239
  - lib/tina4/cli.rb
240
+ - lib/tina4/constants.rb
241
+ - lib/tina4/container.rb
242
+ - lib/tina4/cors.rb
252
243
  - lib/tina4/crud.rb
253
244
  - lib/tina4/database.rb
245
+ - lib/tina4/database/sqlite3_adapter.rb
254
246
  - lib/tina4/database_result.rb
255
247
  - lib/tina4/debug.rb
256
248
  - lib/tina4/dev.rb
249
+ - lib/tina4/dev_admin.rb
250
+ - lib/tina4/dev_mailbox.rb
257
251
  - lib/tina4/dev_reload.rb
258
252
  - lib/tina4/drivers/firebird_driver.rb
259
253
  - lib/tina4/drivers/mssql_driver.rb
@@ -261,23 +255,53 @@ files:
261
255
  - lib/tina4/drivers/postgres_driver.rb
262
256
  - lib/tina4/drivers/sqlite_driver.rb
263
257
  - lib/tina4/env.rb
258
+ - lib/tina4/error_overlay.rb
259
+ - lib/tina4/events.rb
264
260
  - lib/tina4/field_types.rb
261
+ - lib/tina4/frond.rb
262
+ - lib/tina4/gallery/auth/meta.json
263
+ - lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb
264
+ - lib/tina4/gallery/database/meta.json
265
+ - lib/tina4/gallery/database/src/routes/api/gallery_db.rb
266
+ - lib/tina4/gallery/error-overlay/meta.json
267
+ - lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb
268
+ - lib/tina4/gallery/orm/meta.json
269
+ - lib/tina4/gallery/orm/src/routes/api/gallery_products.rb
270
+ - lib/tina4/gallery/queue/meta.json
271
+ - lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb
272
+ - lib/tina4/gallery/rest-api/meta.json
273
+ - lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb
274
+ - lib/tina4/gallery/templates/meta.json
275
+ - lib/tina4/gallery/templates/src/routes/gallery_page.rb
276
+ - lib/tina4/gallery/templates/src/templates/gallery_page.twig
265
277
  - lib/tina4/graphql.rb
278
+ - lib/tina4/health.rb
279
+ - lib/tina4/html_element.rb
266
280
  - lib/tina4/localization.rb
281
+ - lib/tina4/log.rb
282
+ - lib/tina4/messenger.rb
267
283
  - lib/tina4/middleware.rb
268
284
  - lib/tina4/migration.rb
269
285
  - lib/tina4/orm.rb
270
286
  - lib/tina4/public/css/tina4.css
271
287
  - lib/tina4/public/css/tina4.min.css
272
- - lib/tina4/public/js/tina4.js
273
- - lib/tina4/public/js/tina4helper.js
288
+ - lib/tina4/public/favicon.ico
289
+ - lib/tina4/public/images/logo.svg
290
+ - lib/tina4/public/images/tina4-logo-icon.webp
291
+ - lib/tina4/public/js/frond.min.js
292
+ - lib/tina4/public/js/tina4-dev-admin.min.js
293
+ - lib/tina4/public/js/tina4.min.js
294
+ - lib/tina4/public/swagger/index.html
295
+ - lib/tina4/public/swagger/oauth2-redirect.html
274
296
  - lib/tina4/queue.rb
275
297
  - lib/tina4/queue_backends/kafka_backend.rb
276
298
  - lib/tina4/queue_backends/lite_backend.rb
277
299
  - lib/tina4/queue_backends/rabbitmq_backend.rb
278
300
  - lib/tina4/rack_app.rb
301
+ - lib/tina4/rate_limiter.rb
279
302
  - lib/tina4/request.rb
280
303
  - lib/tina4/response.rb
304
+ - lib/tina4/response_cache.rb
281
305
  - lib/tina4/router.rb
282
306
  - lib/tina4/scss/tina4css/_alerts.scss
283
307
  - lib/tina4/scss/tina4css/_badges.scss
@@ -297,16 +321,25 @@ files:
297
321
  - lib/tina4/scss/tina4css/tina4.scss
298
322
  - lib/tina4/scss_compiler.rb
299
323
  - lib/tina4/seeder.rb
324
+ - lib/tina4/service_runner.rb
300
325
  - lib/tina4/session.rb
301
326
  - lib/tina4/session_handlers/file_handler.rb
302
327
  - lib/tina4/session_handlers/mongo_handler.rb
303
328
  - lib/tina4/session_handlers/redis_handler.rb
329
+ - lib/tina4/session_handlers/valkey_handler.rb
330
+ - lib/tina4/shutdown.rb
331
+ - lib/tina4/sql_translation.rb
304
332
  - lib/tina4/swagger.rb
305
333
  - lib/tina4/template.rb
306
334
  - lib/tina4/templates/base.twig
335
+ - lib/tina4/templates/errors/302.twig
336
+ - lib/tina4/templates/errors/401.twig
307
337
  - lib/tina4/templates/errors/403.twig
308
338
  - lib/tina4/templates/errors/404.twig
309
339
  - lib/tina4/templates/errors/500.twig
340
+ - lib/tina4/templates/errors/502.twig
341
+ - lib/tina4/templates/errors/503.twig
342
+ - lib/tina4/templates/errors/base.twig
310
343
  - lib/tina4/testing.rb
311
344
  - lib/tina4/version.rb
312
345
  - lib/tina4/webserver.rb
@@ -1,134 +0,0 @@
1
- /**
2
- * Tina4 CSS — Lightweight JS components
3
- * Replaces Bootstrap's JavaScript for: modals, alerts, navbar toggler
4
- * ~3KB unminified, zero dependencies
5
- */
6
- (function () {
7
- "use strict";
8
-
9
- // ── Modals ──────────────────────────────────────────────────
10
- // Usage: <button data-t4-toggle="modal" data-t4-target="#myModal">Open</button>
11
- // <button data-t4-dismiss="modal">Close</button>
12
- // Also supports Bootstrap syntax: data-bs-toggle, data-bs-target, data-bs-dismiss
13
-
14
- function getModalEl(selector) {
15
- if (!selector) return null;
16
- return document.querySelector(selector);
17
- }
18
-
19
- function openModal(modal) {
20
- if (!modal) return;
21
- // Create backdrop if not exists
22
- var backdrop = modal._t4Backdrop;
23
- if (!backdrop) {
24
- backdrop = document.createElement("div");
25
- backdrop.className = "modal-backdrop";
26
- document.body.appendChild(backdrop);
27
- modal._t4Backdrop = backdrop;
28
- backdrop.addEventListener("click", function () {
29
- closeModal(modal);
30
- });
31
- }
32
- modal.style.display = "block";
33
- backdrop.style.display = "block";
34
- // Force reflow then add .show for transition
35
- void modal.offsetHeight;
36
- modal.classList.add("show");
37
- backdrop.classList.add("show");
38
- document.body.style.overflow = "hidden";
39
- // Focus trap — focus first focusable element
40
- var focusable = modal.querySelector("input, select, textarea, button, [tabindex]");
41
- if (focusable) focusable.focus();
42
- }
43
-
44
- function closeModal(modal) {
45
- if (!modal) return;
46
- modal.classList.remove("show");
47
- var backdrop = modal._t4Backdrop;
48
- if (backdrop) backdrop.classList.remove("show");
49
- // Wait for transition
50
- setTimeout(function () {
51
- modal.style.display = "none";
52
- if (backdrop) backdrop.style.display = "none";
53
- document.body.style.overflow = "";
54
- }, 150);
55
- }
56
-
57
- // Delegated click handler for modal triggers
58
- document.addEventListener("click", function (e) {
59
- var trigger = e.target.closest("[data-t4-toggle='modal'], [data-bs-toggle='modal']");
60
- if (trigger) {
61
- e.preventDefault();
62
- var target = trigger.getAttribute("data-t4-target") || trigger.getAttribute("data-bs-target") || trigger.getAttribute("href");
63
- var modal = getModalEl(target);
64
- if (modal) openModal(modal);
65
- return;
66
- }
67
-
68
- // Dismiss button
69
- var dismiss = e.target.closest("[data-t4-dismiss='modal'], [data-bs-dismiss='modal'], .btn-close");
70
- if (dismiss) {
71
- var modal = dismiss.closest(".modal");
72
- if (modal) closeModal(modal);
73
- return;
74
- }
75
- });
76
-
77
- // ESC key closes top modal
78
- document.addEventListener("keydown", function (e) {
79
- if (e.key === "Escape") {
80
- var modals = document.querySelectorAll(".modal.show");
81
- if (modals.length > 0) closeModal(modals[modals.length - 1]);
82
- }
83
- });
84
-
85
- // ── Alerts (dismissible) ────────────────────────────────────
86
- // Usage: <div class="alert alert-danger alert-dismissible">
87
- // Message <button class="btn-close" data-t4-dismiss="alert">&times;</button>
88
- // </div>
89
-
90
- document.addEventListener("click", function (e) {
91
- var dismiss = e.target.closest("[data-t4-dismiss='alert'], [data-bs-dismiss='alert']");
92
- if (dismiss) {
93
- var alert = dismiss.closest(".alert");
94
- if (alert) {
95
- alert.style.opacity = "0";
96
- setTimeout(function () { alert.remove(); }, 150);
97
- }
98
- }
99
- });
100
-
101
- // ── Navbar toggler ──────────────────────────────────────────
102
- // Usage: <button class="navbar-toggler" data-t4-toggle="collapse" data-t4-target="#navContent">
103
- // &#9776;
104
- // </button>
105
- // <div class="navbar-collapse collapse" id="navContent">...</div>
106
-
107
- document.addEventListener("click", function (e) {
108
- var toggler = e.target.closest("[data-t4-toggle='collapse'], [data-bs-toggle='collapse']");
109
- if (toggler) {
110
- e.preventDefault();
111
- var target = toggler.getAttribute("data-t4-target") || toggler.getAttribute("data-bs-target") || toggler.getAttribute("href");
112
- var el = document.querySelector(target);
113
- if (el) {
114
- el.classList.toggle("show");
115
- }
116
- }
117
- });
118
-
119
- // ── Programmatic API ────────────────────────────────────────
120
- // window.tina4.modal.open("#myModal")
121
- // window.tina4.modal.close("#myModal")
122
-
123
- window.tina4 = window.tina4 || {};
124
- window.tina4.modal = {
125
- open: function (selector) {
126
- var modal = typeof selector === "string" ? document.querySelector(selector) : selector;
127
- openModal(modal);
128
- },
129
- close: function (selector) {
130
- var modal = typeof selector === "string" ? document.querySelector(selector) : selector;
131
- closeModal(modal);
132
- }
133
- };
134
- })();