tina4ruby 0.5.2 → 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 (90) 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/cors.rb +74 -0
  11. data/lib/tina4/database/sqlite3_adapter.rb +139 -0
  12. data/lib/tina4/database.rb +43 -7
  13. data/lib/tina4/debug.rb +4 -79
  14. data/lib/tina4/dev_admin.rb +1162 -0
  15. data/lib/tina4/dev_mailbox.rb +191 -0
  16. data/lib/tina4/dev_reload.rb +9 -9
  17. data/lib/tina4/drivers/firebird_driver.rb +19 -3
  18. data/lib/tina4/drivers/mssql_driver.rb +3 -3
  19. data/lib/tina4/drivers/mysql_driver.rb +4 -4
  20. data/lib/tina4/drivers/postgres_driver.rb +9 -2
  21. data/lib/tina4/drivers/sqlite_driver.rb +1 -1
  22. data/lib/tina4/env.rb +42 -2
  23. data/lib/tina4/error_overlay.rb +252 -0
  24. data/lib/tina4/events.rb +90 -0
  25. data/lib/tina4/field_types.rb +4 -0
  26. data/lib/tina4/frond.rb +1336 -0
  27. data/lib/tina4/gallery/auth/meta.json +1 -0
  28. data/lib/tina4/gallery/auth/src/routes/api/gallery_auth.rb +114 -0
  29. data/lib/tina4/gallery/database/meta.json +1 -0
  30. data/lib/tina4/gallery/database/src/routes/api/gallery_db.rb +43 -0
  31. data/lib/tina4/gallery/error-overlay/meta.json +1 -0
  32. data/lib/tina4/gallery/error-overlay/src/routes/api/gallery_crash.rb +17 -0
  33. data/lib/tina4/gallery/orm/meta.json +1 -0
  34. data/lib/tina4/gallery/orm/src/routes/api/gallery_products.rb +16 -0
  35. data/lib/tina4/gallery/queue/meta.json +1 -0
  36. data/lib/tina4/gallery/queue/src/routes/api/gallery_queue.rb +27 -0
  37. data/lib/tina4/gallery/rest-api/meta.json +1 -0
  38. data/lib/tina4/gallery/rest-api/src/routes/api/gallery_hello.rb +14 -0
  39. data/lib/tina4/gallery/templates/meta.json +1 -0
  40. data/lib/tina4/gallery/templates/src/routes/gallery_page.rb +12 -0
  41. data/lib/tina4/gallery/templates/src/templates/gallery_page.twig +257 -0
  42. data/lib/tina4/health.rb +39 -0
  43. data/lib/tina4/html_element.rb +148 -0
  44. data/lib/tina4/localization.rb +2 -2
  45. data/lib/tina4/log.rb +203 -0
  46. data/lib/tina4/messenger.rb +484 -0
  47. data/lib/tina4/migration.rb +132 -29
  48. data/lib/tina4/orm.rb +337 -31
  49. data/lib/tina4/public/css/tina4.css +178 -1
  50. data/lib/tina4/public/css/tina4.min.css +1 -2
  51. data/lib/tina4/public/favicon.ico +0 -0
  52. data/lib/tina4/public/images/logo.svg +5 -0
  53. data/lib/tina4/public/images/tina4-logo-icon.webp +0 -0
  54. data/lib/tina4/public/js/frond.min.js +420 -0
  55. data/lib/tina4/public/js/tina4-dev-admin.min.js +367 -0
  56. data/lib/tina4/public/js/tina4.min.js +93 -0
  57. data/lib/tina4/public/swagger/index.html +90 -0
  58. data/lib/tina4/public/swagger/oauth2-redirect.html +63 -0
  59. data/lib/tina4/queue.rb +40 -4
  60. data/lib/tina4/queue_backends/lite_backend.rb +88 -0
  61. data/lib/tina4/rack_app.rb +314 -23
  62. data/lib/tina4/rate_limiter.rb +123 -0
  63. data/lib/tina4/request.rb +61 -15
  64. data/lib/tina4/response.rb +54 -24
  65. data/lib/tina4/response_cache.rb +134 -0
  66. data/lib/tina4/router.rb +90 -15
  67. data/lib/tina4/scss_compiler.rb +2 -2
  68. data/lib/tina4/seeder.rb +56 -61
  69. data/lib/tina4/service_runner.rb +303 -0
  70. data/lib/tina4/session.rb +85 -0
  71. data/lib/tina4/session_handlers/mongo_handler.rb +1 -1
  72. data/lib/tina4/session_handlers/valkey_handler.rb +43 -0
  73. data/lib/tina4/shutdown.rb +84 -0
  74. data/lib/tina4/sql_translation.rb +295 -0
  75. data/lib/tina4/template.rb +36 -6
  76. data/lib/tina4/templates/base.twig +2 -2
  77. data/lib/tina4/templates/errors/302.twig +14 -0
  78. data/lib/tina4/templates/errors/401.twig +9 -0
  79. data/lib/tina4/templates/errors/403.twig +22 -15
  80. data/lib/tina4/templates/errors/404.twig +22 -15
  81. data/lib/tina4/templates/errors/500.twig +31 -15
  82. data/lib/tina4/templates/errors/502.twig +9 -0
  83. data/lib/tina4/templates/errors/503.twig +12 -0
  84. data/lib/tina4/templates/errors/base.twig +37 -0
  85. data/lib/tina4/version.rb +1 -1
  86. data/lib/tina4/webserver.rb +28 -18
  87. data/lib/tina4.rb +57 -21
  88. metadata +51 -19
  89. data/lib/tina4/public/js/tina4.js +0 -134
  90. 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,13 +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"
29
38
  require_relative "tina4/container"
30
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"
31
50
 
32
51
  module Tina4
33
52
  # ── Lazy-loaded: database drivers ─────────────────────────────────────
@@ -43,7 +62,8 @@ module Tina4
43
62
  module SessionHandlers
44
63
  autoload :FileHandler, File.expand_path("tina4/session_handlers/file_handler", __dir__)
45
64
  autoload :RedisHandler, File.expand_path("tina4/session_handlers/redis_handler", __dir__)
46
- 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__)
47
67
  end
48
68
 
49
69
  # ── Lazy-loaded: queue backends ───────────────────────────────────────
@@ -73,27 +93,38 @@ module Tina4
73
93
  autoload :ScssCompiler, File.expand_path("tina4/scss_compiler", __dir__)
74
94
  autoload :DevReload, File.expand_path("tina4/dev_reload", __dir__)
75
95
  autoload :FakeData, File.expand_path("tina4/seeder", __dir__)
76
- autoload :Seeder, File.expand_path("tina4/seeder", __dir__)
77
96
  autoload :WSDL, File.expand_path("tina4/wsdl", __dir__)
78
97
  BANNER = <<~'BANNER'
79
98
 
80
- ████████╗██╗███╗ ██╗ █████╗ ██╗ ██╗
81
- ╚══██╔══╝██║████╗ ██║██╔══██╗██║ ██║
82
- ██║ ██║██╔██╗ ██║███████║███████║
83
- ██║ ██║██║╚██╗██║██╔══██║╚════██║
84
- ██║ ██║██║ ╚████║██║ ██║ ██║
85
- ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝
99
+ ______ _ __ __
100
+ /_ __/(_)___ ____ _/ // /
101
+ / / / / __ \/ __ `/ // /_
102
+ / / / / / / / /_/ /__ __/
103
+ /_/ /_/_/ /_/\__,_/ /_/
86
104
  BANNER
87
105
 
88
106
  class << self
89
107
  attr_accessor :root_dir, :database
90
108
 
91
- def print_banner
92
- puts "\e[35m#{BANNER}\e[0m"
93
- 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})"
94
125
  puts ""
95
126
  rescue
96
- puts "\e[35mTINA4 Ruby v#{VERSION}\e[0m"
127
+ puts "#{color}TINA4 Ruby v#{VERSION}#{reset}"
97
128
  end
98
129
 
99
130
  def initialize!(root_dir = Dir.pwd)
@@ -103,11 +134,11 @@ module Tina4
103
134
  print_banner
104
135
 
105
136
  # Load environment
106
- Tina4::Env.load(root_dir)
137
+ Tina4::Env.load_env(root_dir)
107
138
 
108
139
  # Setup debug logging
109
- Tina4::Debug.setup(root_dir)
110
- Tina4::Debug.info("Tina4 Ruby v#{VERSION} initializing...")
140
+ Tina4::Log.setup(root_dir)
141
+ Tina4::Log.info("Tina4 Ruby v#{VERSION} initializing...")
111
142
 
112
143
  # Setup auth keys
113
144
  Tina4::Auth.setup(root_dir)
@@ -121,7 +152,7 @@ module Tina4
121
152
  # Auto-discover routes
122
153
  auto_discover(root_dir)
123
154
 
124
- Tina4::Debug.info("Tina4 initialized successfully")
155
+ Tina4::Log.info("Tina4 initialized successfully")
125
156
  end
126
157
 
127
158
  # DSL methods for route registration
@@ -218,6 +249,11 @@ module Tina4
218
249
  Tina4::Localization.t(key, **options)
219
250
  end
220
251
 
252
+ # Service runner DSL
253
+ def service(name, options = {}, &block)
254
+ Tina4::ServiceRunner.register(name, nil, options, &block)
255
+ end
256
+
221
257
  # DI container shortcuts
222
258
  def register(name, instance = nil, &block)
223
259
  Tina4::Container.register(name, instance, &block)
@@ -250,9 +286,9 @@ module Tina4
250
286
  if db_url && !db_url.empty?
251
287
  begin
252
288
  @database = Tina4::Database.new(db_url)
253
- Tina4::Debug.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
289
+ Tina4::Log.info("Database connected: #{db_url.sub(/:[^:@]+@/, ':***@')}")
254
290
  rescue => e
255
- Tina4::Debug.error("Database connection failed: #{e.message}")
291
+ Tina4::Log.error("Database connection failed: #{e.message}")
256
292
  end
257
293
  end
258
294
  end
@@ -266,9 +302,9 @@ module Tina4
266
302
  Dir.glob(File.join(full_dir, "**/*.rb")).sort.each do |file|
267
303
  begin
268
304
  load file
269
- Tina4::Debug.debug("Auto-loaded: #{file}")
305
+ Tina4::Log.debug("Auto-loaded: #{file}")
270
306
  rescue => e
271
- Tina4::Debug.error("Failed to load #{file}: #{e.message}")
307
+ Tina4::Log.error("Failed to load #{file}: #{e.message}")
272
308
  end
273
309
  end
274
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.2
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,24 +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
252
241
  - lib/tina4/container.rb
242
+ - lib/tina4/cors.rb
253
243
  - lib/tina4/crud.rb
254
244
  - lib/tina4/database.rb
245
+ - lib/tina4/database/sqlite3_adapter.rb
255
246
  - lib/tina4/database_result.rb
256
247
  - lib/tina4/debug.rb
257
248
  - lib/tina4/dev.rb
249
+ - lib/tina4/dev_admin.rb
250
+ - lib/tina4/dev_mailbox.rb
258
251
  - lib/tina4/dev_reload.rb
259
252
  - lib/tina4/drivers/firebird_driver.rb
260
253
  - lib/tina4/drivers/mssql_driver.rb
@@ -262,23 +255,53 @@ files:
262
255
  - lib/tina4/drivers/postgres_driver.rb
263
256
  - lib/tina4/drivers/sqlite_driver.rb
264
257
  - lib/tina4/env.rb
258
+ - lib/tina4/error_overlay.rb
259
+ - lib/tina4/events.rb
265
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
266
277
  - lib/tina4/graphql.rb
278
+ - lib/tina4/health.rb
279
+ - lib/tina4/html_element.rb
267
280
  - lib/tina4/localization.rb
281
+ - lib/tina4/log.rb
282
+ - lib/tina4/messenger.rb
268
283
  - lib/tina4/middleware.rb
269
284
  - lib/tina4/migration.rb
270
285
  - lib/tina4/orm.rb
271
286
  - lib/tina4/public/css/tina4.css
272
287
  - lib/tina4/public/css/tina4.min.css
273
- - lib/tina4/public/js/tina4.js
274
- - 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
275
296
  - lib/tina4/queue.rb
276
297
  - lib/tina4/queue_backends/kafka_backend.rb
277
298
  - lib/tina4/queue_backends/lite_backend.rb
278
299
  - lib/tina4/queue_backends/rabbitmq_backend.rb
279
300
  - lib/tina4/rack_app.rb
301
+ - lib/tina4/rate_limiter.rb
280
302
  - lib/tina4/request.rb
281
303
  - lib/tina4/response.rb
304
+ - lib/tina4/response_cache.rb
282
305
  - lib/tina4/router.rb
283
306
  - lib/tina4/scss/tina4css/_alerts.scss
284
307
  - lib/tina4/scss/tina4css/_badges.scss
@@ -298,16 +321,25 @@ files:
298
321
  - lib/tina4/scss/tina4css/tina4.scss
299
322
  - lib/tina4/scss_compiler.rb
300
323
  - lib/tina4/seeder.rb
324
+ - lib/tina4/service_runner.rb
301
325
  - lib/tina4/session.rb
302
326
  - lib/tina4/session_handlers/file_handler.rb
303
327
  - lib/tina4/session_handlers/mongo_handler.rb
304
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
305
332
  - lib/tina4/swagger.rb
306
333
  - lib/tina4/template.rb
307
334
  - lib/tina4/templates/base.twig
335
+ - lib/tina4/templates/errors/302.twig
336
+ - lib/tina4/templates/errors/401.twig
308
337
  - lib/tina4/templates/errors/403.twig
309
338
  - lib/tina4/templates/errors/404.twig
310
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
311
343
  - lib/tina4/testing.rb
312
344
  - lib/tina4/version.rb
313
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
- })();