@rip-lang/server 1.4.6 → 1.4.8

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.
package/browse.rip CHANGED
@@ -5,15 +5,15 @@
5
5
  # Built-in file browser when no index.rip or index.ts exists in the target
6
6
  # directory. Activated automatically by `rip server` when no app entry is found.
7
7
  #
8
- # Single-click serves files natively (HTML renders, images display, etc.).
9
- # Double-click (appends ?) shows syntax-highlighted source for text files.
10
- # Markdown always renders. Directories show a listing with index file detection.
8
+ # Text files get syntax-highlighted by default. HTML files render natively
9
+ # (double-click adds ?view to see source). Markdown renders. Binary files
10
+ # (images, PDFs) serve natively. Directories show a listing.
11
11
  # ==============================================================================
12
12
 
13
13
  import { use, start, notFound } from '@rip-lang/server'
14
14
  import { statSync } from 'node:fs'
15
15
  import { join, resolve, basename } from 'node:path'
16
- import { renderDirectoryListing, renderMarkdown, renderTextFile, isTextFile, serveRipHighlightGrammar, findIndexFile } from './serving/static.rip'
16
+ import { renderDirectoryListing, serveBrowseFile, serveRipHighlightGrammar, findIndexFile } from './serving/static.rip'
17
17
 
18
18
  root = resolve(process.env.APP_BASE_DIR or process.cwd())
19
19
  rootSlash = root + '/'
@@ -27,7 +27,6 @@ notFound ->
27
27
  url = new URL(@req.url)
28
28
  reqPath = try decodeURIComponent(url.pathname) catch then url.pathname
29
29
  path = resolve(root, reqPath.slice(1))
30
- viewSource = url.search is '?view'
31
30
 
32
31
  unless path is root or path.startsWith(rootSlash)
33
32
  return new Response 'Not Found', { status: 404 }
@@ -40,14 +39,8 @@ notFound ->
40
39
  if stat.isFile()
41
40
  accept = @req.header('accept') or ''
42
41
  if accept.includes('text/html')
43
- if path.endsWith('.md') and not viewSource
44
- try
45
- html = renderMarkdown(path)
46
- return new Response html, { headers: { 'Content-Type': 'text/html; charset=UTF-8' } }
47
- if viewSource and isTextFile(path)
48
- try
49
- html = renderTextFile(path)
50
- return new Response html, { headers: { 'Content-Type': 'text/html; charset=UTF-8' } }
42
+ res = serveBrowseFile(path, url)
43
+ return res if res
51
44
  return @send path
52
45
 
53
46
  if stat.isDirectory()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rip-lang/server",
3
- "version": "1.4.6",
3
+ "version": "1.4.8",
4
4
  "description": "Bun-native content server: static sites, apps, HTTP proxy, and TCP/TLS passthrough",
5
5
  "type": "module",
6
6
  "main": "api.rip",
@@ -189,7 +189,7 @@ export renderDirectoryListing = (reqPath, fsPath, rootName) ->
189
189
  </table>
190
190
  </div></div>
191
191
  <script>
192
- !function(){var p,c,ox=14,oy=14;function mk(){if(p)return p;p=document.createElement('div');p.className='pop';p.innerHTML='<div class="pop-b"></div>';document.body.appendChild(p);return p}function show(a,e){var s=a.dataset.src;if(!s)return;var el=mk(),b=el.firstChild;b.innerHTML='';var img=new Image();img.src=s;b.appendChild(img);c=a;el.classList.add('on');place(e.clientX,e.clientY)}function hide(){c=null;if(p)p.classList.remove('on')}function place(x,y){if(!p)return;var r=p.getBoundingClientRect(),m=12,l=x+ox,t=y+oy;if(l+r.width>innerWidth-m)l=x-r.width-ox;if(t+r.height>innerHeight-m)t=innerHeight-r.height-m;if(l<m)l=m;if(t<m)t=m;p.style.left=l+'px';p.style.top=t+'px'}document.addEventListener('mouseover',function(e){var a=e.target.closest('[data-pv]');if(a)show(a,e)});document.addEventListener('mousemove',function(e){if(c)place(e.clientX,e.clientY)});document.addEventListener('mouseout',function(e){var a=e.target.closest('[data-pv]');if(a&&a===c)hide()});document.addEventListener('scroll',hide,{passive:true});document.addEventListener('keydown',function(e){if(e.key==='Escape')hide()});window.addEventListener('blur',hide);var dt=0;document.addEventListener('click',function(e){var a=e.target.closest('td.nm a');if(!a)return;var h=a.getAttribute('href');if(!h||h.endsWith('/'))return;e.preventDefault();clearTimeout(dt);dt=setTimeout(function(){location.href=h},250)});document.addEventListener('dblclick',function(e){var a=e.target.closest('td.nm a');if(!a)return;var h=a.getAttribute('href');if(!h||h.endsWith('/'))return;e.preventDefault();clearTimeout(dt);location.href=h+'?view'})}();
192
+ !function(){var p,c,ox=14,oy=14;function mk(){if(p)return p;p=document.createElement('div');p.className='pop';p.innerHTML='<div class="pop-b"></div>';document.body.appendChild(p);return p}function show(a,e){var s=a.dataset.src;if(!s)return;var el=mk(),b=el.firstChild;b.innerHTML='';var img=new Image();img.src=s;b.appendChild(img);c=a;el.classList.add('on');place(e.clientX,e.clientY)}function hide(){c=null;if(p)p.classList.remove('on')}function place(x,y){if(!p)return;var r=p.getBoundingClientRect(),m=12,l=x+ox,t=y+oy;if(l+r.width>innerWidth-m)l=x-r.width-ox;if(t+r.height>innerHeight-m)t=innerHeight-r.height-m;if(l<m)l=m;if(t<m)t=m;p.style.left=l+'px';p.style.top=t+'px'}document.addEventListener('mouseover',function(e){var a=e.target.closest('[data-pv]');if(a)show(a,e)});document.addEventListener('mousemove',function(e){if(c)place(e.clientX,e.clientY)});document.addEventListener('mouseout',function(e){var a=e.target.closest('[data-pv]');if(a&&a===c)hide()});document.addEventListener('scroll',hide,{passive:true});document.addEventListener('keydown',function(e){if(e.key==='Escape')hide()});window.addEventListener('blur',hide);var dt=0,hx=/\.html?$/i;document.addEventListener('click',function(e){var a=e.target.closest('td.nm a');if(!a)return;var h=a.getAttribute('href');if(!h||h.endsWith('/'))return;if(!hx.test(h))return;e.preventDefault();clearTimeout(dt);dt=setTimeout(function(){location.href=h},250)});document.addEventListener('dblclick',function(e){var a=e.target.closest('td.nm a');if(!a)return;var h=a.getAttribute('href');if(!h||h.endsWith('/'))return;if(!hx.test(h))return;e.preventDefault();clearTimeout(dt);location.href=h+'?view'})}();
193
193
  </script>
194
194
  </body>
195
195
  </html>
@@ -319,6 +319,19 @@ export renderTextFile = (filePath) ->
319
319
  </html>
320
320
  """
321
321
 
322
+ # --- Browse file response (shared by browse.rip and serveStaticRoute) ---
323
+
324
+ export serveBrowseFile = (filePath, url) ->
325
+ viewSource = url.search is '?view'
326
+ isHtml = filePath.endsWith('.html') or filePath.endsWith('.htm')
327
+ if filePath.endsWith('.md') and not viewSource
328
+ html = renderMarkdown(filePath)
329
+ return new Response(html, { headers: { 'content-type': 'text/html; charset=UTF-8' } })
330
+ if isTextFile(filePath) and (viewSource or not isHtml)
331
+ html = renderTextFile(filePath)
332
+ return new Response(html, { headers: { 'content-type': 'text/html; charset=UTF-8' } })
333
+ null
334
+
322
335
  # --- Static file serving ---
323
336
 
324
337
  export INDEX_FILES = ['index.html', 'index.rip', 'index.ts', 'index.tsx', 'index.jsx', 'index.js']
@@ -376,13 +389,8 @@ export serveStaticRoute = (req, url, route) ->
376
389
  return new Response(html, { headers: { 'content-type': 'text/html; charset=UTF-8' } })
377
390
  if stat.isFile()
378
391
  if route.browse and acceptsHtml(req)
379
- viewSource = url.search is '?view'
380
- if filePath.endsWith('.md') and not viewSource
381
- html = renderMarkdown(filePath)
382
- return new Response(html, { headers: { 'content-type': 'text/html; charset=UTF-8' } })
383
- if viewSource and isTextFile(filePath)
384
- html = renderTextFile(filePath)
385
- return new Response(html, { headers: { 'content-type': 'text/html; charset=UTF-8' } })
392
+ res = serveBrowseFile(filePath, url)
393
+ return res if res
386
394
  file = Bun.file(filePath)
387
395
  return new Response(file, { headers: { 'content-type': mimeType(filePath) } })
388
396