@rip-lang/server 1.3.78 → 1.3.79

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 (2) hide show
  1. package/default.rip +138 -27
  2. package/package.json +1 -1
package/default.rip CHANGED
@@ -17,10 +17,11 @@
17
17
  import { use, start, notFound } from '@rip-lang/server'
18
18
  import { serve } from '@rip-lang/server/middleware'
19
19
  import { statSync, readdirSync } from 'node:fs'
20
- import { join, resolve } from 'node:path'
20
+ import { join, resolve, basename } from 'node:path'
21
21
 
22
22
  root = resolve(process.env.APP_BASE_DIR or process.cwd())
23
23
  rootSlash = root + '/'
24
+ rootName = basename(root) or root
24
25
 
25
26
  use serve dir: root, bundle: [], watch: true
26
27
 
@@ -37,10 +38,17 @@ fmtSize = (n) ->
37
38
  "#{n.toFixed(1)} TB"
38
39
 
39
40
  fmtDate = (ms) ->
40
- new Date(ms).toLocaleString undefined,
41
- year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit'
42
-
43
- ICONS =
41
+ d = new Date(ms)
42
+ mo = d.toLocaleString('en', { month: 'short' })
43
+ day = String(d.getDate()).padStart(2, '0')
44
+ year = d.getFullYear()
45
+ hour = d.getHours()
46
+ min = String(d.getMinutes()).padStart(2, '0')
47
+ ampm = if hour >= 12 then 'PM' else 'AM'
48
+ hour = hour % 12 or 12
49
+ "#{mo} #{day}, #{year} at #{hour}:#{min} #{ampm}"
50
+
51
+ EXT_ICONS =
44
52
  html: '🌐', htm: '🌐', css: '🎨', js: '📜', mjs: '📜', ts: '📜'
45
53
  json: '📋', rip: '⚡', md: '📝', txt: '📝', pdf: '📕'
46
54
  png: '🖼️', jpg: '🖼️', jpeg: '🖼️', gif: '🖼️', svg: '🖼️', webp: '🖼️', ico: '🖼️'
@@ -48,11 +56,20 @@ ICONS =
48
56
  mp3: '🎵', ogg: '🎵', wav: '🎵', mp4: '🎬', webm: '🎬'
49
57
  woff: '🔤', woff2: '🔤', ttf: '🔤', otf: '🔤'
50
58
 
51
- icon = (name) ->
52
- ICONS[name.slice(name.lastIndexOf('.') + 1).toLowerCase()] or '📄'
59
+ fileIcon = (name) ->
60
+ EXT_ICONS[name.slice(name.lastIndexOf('.') + 1).toLowerCase()] or '📄'
61
+
62
+ row = (ic, href, label, size = '', date = '') ->
63
+ "<tr><td class=\"ic\">#{ic}</td><td><a href=\"#{esc href}\">#{esc label}</a></td><td class=\"sz\">#{size}</td><td class=\"dt\">#{date}</td></tr>"
53
64
 
54
- tr = (ic, href, label, size = '', date = '') ->
55
- "<tr><td class=\"i\">#{ic}</td><td><a href=\"#{esc href}\">#{esc label}</a></td><td class=\"s\">#{size}</td><td class=\"d\">#{date}</td></tr>"
65
+ breadcrumbs = (reqPath) ->
66
+ parts = reqPath.split('/').filter((p) -> p)
67
+ crumbs = "<a href=\"/\">#{esc rootName}</a>"
68
+ href = ''
69
+ for part in parts
70
+ href += "/#{part}"
71
+ crumbs += " <span class=\"sep\">/</span> <a href=\"#{href}/\">#{esc part}</a>"
72
+ crumbs
56
73
 
57
74
  # --- Directory Index ---
58
75
 
@@ -67,45 +84,139 @@ renderIndex = (reqPath, fsPath) ->
67
84
  files.sort()
68
85
 
69
86
  rows = []
70
- rows.push tr('📁', '../', 'Parent Directory') if reqPath isnt '/'
87
+ rows.push row('📁', '../', '..') if reqPath isnt '/'
71
88
  for d in dirs
72
- rows.push tr('📁', "#{d}/", "#{d}/")
89
+ rows.push row('📁', "#{d}/", d)
73
90
  for f in files
74
91
  try
75
92
  s = statSync(join(fsPath, f))
76
- rows.push tr(icon(f), f, f, fmtSize(s.size), fmtDate(s.mtimeMs))
93
+ rows.push row(fileIcon(f), f, f, fmtSize(s.size), fmtDate(s.mtimeMs))
77
94
  catch
78
- rows.push tr(icon(f), f, f)
95
+ rows.push row(fileIcon(f), f, f)
96
+
97
+ numDirs = dirs.length
98
+ numFiles = files.length
99
+ summary = []
100
+ summary.push "#{numDirs} folder#{if numDirs isnt 1 then 's' else ''}" if numDirs > 0
101
+ summary.push "#{numFiles} file#{if numFiles isnt 1 then 's' else ''}" if numFiles > 0
79
102
 
80
- title = esc(reqPath)
81
103
  """
82
104
  <!DOCTYPE html>
83
105
  <html lang="en">
84
106
  <head>
85
107
  <meta charset="UTF-8">
86
108
  <meta name="viewport" content="width=device-width, initial-scale=1">
87
- <title>Index of #{title}</title>
109
+ <title>Index of #{esc reqPath}</title>
88
110
  <style>
89
- *{margin:0;padding:0;box-sizing:border-box}
90
- body{font-family:ui-monospace,'SF Mono',Menlo,Monaco,monospace;background:#fafafa;color:#333;padding:24px 40px}
91
- h1{font-size:16px;font-weight:600;color:#111;padding:12px 0;border-bottom:2px solid #e1e4e8;margin-bottom:4px}
92
- table{width:100%;border-collapse:collapse}
93
- th{text-align:left;padding:8px 12px;font-size:11px;font-weight:600;color:#888;text-transform:uppercase;letter-spacing:.5px;border-bottom:1px solid #e1e4e8}
94
- td{padding:5px 12px;border-bottom:1px solid #f0f0f0;font-size:13px}
95
- a{color:#0969da;text-decoration:none}a:hover{text-decoration:underline}
96
- tr:hover td{background:#f0f7ff}
97
- .i{width:28px;text-align:center}.s{width:100px;text-align:right;color:#666}.d{width:200px;color:#666}th.s{text-align:right}
98
- @media(max-width:600px){body{padding:16px}.d,th.d{display:none}}
111
+ :root {
112
+ --bg: #f3f6f7;
113
+ --card: #fff;
114
+ --border: #e5e7eb;
115
+ --border-row: #f0f0f0;
116
+ --text: #1f2937;
117
+ --muted: #6b7280;
118
+ --link: #2563eb;
119
+ --link-visited: #7c3aed;
120
+ --hover-bg: #f0f7ff;
121
+ --head-bg: #fafbfc;
122
+ --sep: #cbd5e1;
123
+ }
124
+ @media (prefers-color-scheme: dark) {
125
+ :root {
126
+ --bg: #0d1117;
127
+ --card: #161b22;
128
+ --border: #30363d;
129
+ --border-row: #21262d;
130
+ --text: #c9d1d9;
131
+ --muted: #8b949e;
132
+ --link: #58a6ff;
133
+ --link-visited: #bc8cff;
134
+ --hover-bg: #1c2129;
135
+ --head-bg: #1c2129;
136
+ --sep: #484f58;
137
+ }
138
+ }
139
+ *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
140
+ body {
141
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
142
+ font-size: 15px;
143
+ line-height: 1.5;
144
+ color: var(--text);
145
+ background: var(--bg);
146
+ -webkit-font-smoothing: antialiased;
147
+ }
148
+ .wrap { max-width: 960px; margin: 2.5rem auto; padding: 0 1rem; }
149
+ .card {
150
+ background: var(--card);
151
+ border: 1px solid var(--border);
152
+ border-radius: 8px;
153
+ overflow: hidden;
154
+ box-shadow: 0 1px 3px rgba(0,0,0,.06), 0 1px 2px rgba(0,0,0,.04);
155
+ }
156
+ .bar {
157
+ display: flex;
158
+ align-items: center;
159
+ justify-content: space-between;
160
+ padding: .875rem 1.25rem;
161
+ border-bottom: 1px solid var(--border);
162
+ gap: 1rem;
163
+ }
164
+ .crumbs { font-size: .9375rem; font-weight: 500; }
165
+ .crumbs a { color: var(--link); text-decoration: none; }
166
+ .crumbs a:hover { text-decoration: underline; }
167
+ .sep { color: var(--sep); margin: 0 .3rem; font-weight: 400; }
168
+ .meta { font-size: .8125rem; color: var(--muted); white-space: nowrap; }
169
+ table { width: 100%; border-collapse: collapse; }
170
+ th {
171
+ position: sticky; top: 0;
172
+ background: var(--head-bg);
173
+ text-align: left;
174
+ padding: .625rem 1.25rem;
175
+ font-size: .6875rem;
176
+ font-weight: 600;
177
+ color: var(--muted);
178
+ text-transform: uppercase;
179
+ letter-spacing: .05em;
180
+ border-bottom: 1px solid var(--border);
181
+ }
182
+ td {
183
+ padding: .5rem 1.25rem;
184
+ border-bottom: 1px solid var(--border-row);
185
+ font-size: .875rem;
186
+ vertical-align: middle;
187
+ }
188
+ tr:last-child td { border-bottom: none; }
189
+ tbody tr:hover { background: var(--hover-bg); }
190
+ td a { color: var(--link); text-decoration: none; }
191
+ td a:hover { color: var(--link); text-decoration: underline; }
192
+ td a:visited { color: var(--link-visited); }
193
+ .ic { width: 2.5rem; text-align: center; font-size: 1.125rem; }
194
+ .sz { width: 6rem; text-align: right; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
195
+ .dt { color: var(--muted); white-space: nowrap; }
196
+ th.sz { text-align: right; }
197
+ @media (max-width: 640px) {
198
+ .wrap { margin: 1rem auto; }
199
+ td, th { padding: .5rem .75rem; }
200
+ .dt, th.dt { display: none; }
201
+ .bar { padding: .75rem 1rem; }
202
+ }
99
203
  </style>
100
204
  </head>
101
205
  <body>
102
- <h1>Index of #{title}</h1>
206
+ <div class="wrap">
207
+ <div class="card">
208
+ <div class="bar">
209
+ <div class="crumbs">#{breadcrumbs reqPath}</div>
210
+ <div class="meta">#{summary.join(' &middot; ')}</div>
211
+ </div>
103
212
  <table>
104
- <thead><tr><th></th><th>Name</th><th class="s">Size</th><th class="d">Modified</th></tr></thead>
213
+ <thead><tr><th></th><th>Name</th><th class="sz">Size</th><th class="dt">Modified</th></tr></thead>
105
214
  <tbody>
106
215
  #{rows.join('\n')}
107
216
  </tbody>
108
217
  </table>
218
+ </div>
219
+ </div>
109
220
  </body>
110
221
  </html>
111
222
  """
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/server",
3
- "version": "1.3.78",
3
+ "version": "1.3.79",
4
4
  "description": "Pure Rip web framework and application server",
5
5
  "type": "module",
6
6
  "main": "api.rip",