Files
hi-language.github.io/index.html
2025-09-14 22:18:58 -07:00

358 lines
11 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"/>
<title>Hi Language — Symbolic, WorldFirst</title>
<!-- Tailwind (for layout/spacing/utility) -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- GitHub Markdown skin + HLJS GitHub theme -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@5.8.1/github-markdown-light.min.css"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/styles/github.min.css"/>
<style>
:root{color-scheme:light}
html,body{height:100%}
.no-scrollbar::-webkit-scrollbar{display:none}
.no-scrollbar{-ms-overflow-style:none;scrollbar-width:none}
.markdown-body{font-size:16px;line-height:1.65}
.markdown-body :is(h1,h2,h3){scroll-margin-top:84px}
.markdown-body pre{overflow:auto}
.copy-btn{position:absolute;top:.5rem;right:.5rem;font-size:.75rem;padding:.25rem .5rem;border-radius:.5rem;background:#0f172a;color:#fff;opacity:.9}
.copy-btn:hover{opacity:1}
.toc-link{display:block;padding:.5rem .75rem;border-radius:.75rem}
.toc-link:hover{background:#f3f4f6}
.toc-active{background:#eef2ff}
.brand-dot{display:inline-flex;align-items:center;justify-content:center;height:28px;width:28px;border-radius:9999px;background:#0f172a;color:#fff}
header{backdrop-filter:saturate(180%) blur(8px)}
@media (min-width:1024px){
#contentWrap{grid-template-columns: 280px 1fr}
#sidebarLeft{position:sticky;top:64px;height:calc(100dvh - 64px);transform:none!important}
#sidebarOverlay{display:none!important}
}
</style>
<!-- Alpine + Lucide + MarkdownIt + HLJS -->
<script defer src="https://unpkg.com/alpinejs"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.1.0/dist/markdown-it.min.js"></script>
<script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.11.1/build/highlight.min.js"></script>
</head>
<body class="bg-white text-gray-900 selection:bg-black/10" x-data="{open:false}" @keydown.escape="open=false">
<!-- Topbar -->
<header class="sticky top-0 z-20 bg-white/85 border-b border-gray-200">
<div class="mx-auto max-w-6xl px-4 py-3 grid grid-cols-3 items-center">
<button class="h-8 w-8 rounded-xl bg-gray-100 hover:bg-gray-200 active:scale-[.99] transition flex items-center justify-center"
title="Menu" @click="open=true">
<i data-lucide="panel-left" class="h-5 w-5"></i>
</button>
<div class="justify-self-center text-base font-semibold tracking-tight select-none pointer-events-none">Hi</div>
<div class="justify-self-end"></div>
</div>
</header>
<!-- Content -->
<div id="contentWrap" class="mx-auto max-w-6xl lg:grid relative">
<!-- Left sidebar -->
<aside id="sidebarLeft" class="fixed inset-y-0 left-0 z-50 w-72 max-w-[85vw] bg-white border-r border-gray-200 shadow-xl transform -translate-x-full transition-transform duration-200 ease-out lg:static lg:shadow-none lg:w-auto">
<div class="p-3 border-b flex items-center gap-3">
<span class="brand-dot"></span>
<div class="min-w-0">
<div class="text-sm font-semibold truncate">Hi</div>
<div class="text-xs text-gray-500 truncate">A symbolic language for everyone</div>
</div>
</div>
<nav id="toc" class="p-2 overflow-y-auto no-scrollbar space-y-1 text-sm"></nav>
<div class="p-3 border-t text-xs text-gray-500">
<span>Still in development — more coming soon.</span>
</div>
</aside>
<div id="sidebarOverlay" class="fixed inset-0 z-40 bg-black/20 hidden" :class="open?'':'hidden'" @click="open=false"></div>
<script>
document.addEventListener('alpine:init',()=>{Alpine.effect(()=>{const a=document.getElementById('sidebarLeft');if(!a)return;a.style.transform=Alpine.store('open')?'translateX(0)':''})})
</script>
<!-- Main -->
<main id="main" class="min-h-[calc(100dvh-64px)]">
<section id="suneHtml" class="markdown-body px-4 sm:px-6 lg:px-10 py-8"></section>
</main>
</div>
<script>
document.addEventListener('DOMContentLoaded',()=>{
const md = window.markdownit({html:false,linkify:true,typographer:true,breaks:true})
const slug = s=>s.toLowerCase().trim()
.replace(/["']/g,'')
.replace(/&/g,' and ')
.replace(/[^a-z0-9]+/g,'-')
.replace(/^-+|-+$/g,'')||'sec'
const $ = s=>document.querySelector(s)
const $$ = s=>[...document.querySelectorAll(s)]
const icons = ()=>window.lucide&&lucide.createIcons()
const DOC = String.raw`
# Hi
A new, symbolic programming language inspired by JavaScript — but with a "corely" symbolic design. Fewer keywords, more universal symbols. Hi aims to be approachable worldwide.
> Tip: All code snippets below use JavaScript syntax highlighting while Hi is still evolving.
## Hello, world
\`\`\`js
// This is hello world in Hi
_("Hi world")
\`\`\`
## Symbols youll meet first
- \`: \` declaration
- \`= \` assignment
- \`{ ... }\` a Block (both object and function)
- \`#name\` private field in a Block
- \`_("...")\` print (show text)
- \`? :\` conditional (blocks return last value)
- \`* { ... }\` loop body
- \`>>\` continue, \`^\` break
Blocks are always both an object and a function. They can hold properties and executable code at once.
## Blocks
### Function Block
\`\`\`js
sayHi: {
_("Hi")
}
sayHi() // call the Block like a function
\`\`\`
### Object Block
\`\`\`js
player: {
name: "Orion" // public
#hp: 100 // private
}
_(player.name) // "Orion"
\`\`\`
### Hybrid Block
\`\`\`js
counter: {
#value: 0
inc: {
value = value + 1
_("The count is now: " + value)
}
}
counter.inc() // The count is now: 1
counter.inc() // The count is now: 2
\`\`\`
### Parameters
\`\`\`js
withParams: (str) {
_(str)
}
withParams("Hola") // prints Hola
\`\`\`
## Truthiness and equality
Hi emphasizes clear truthiness:
\`\`\`js
0 // falsy (official false)
!0 // truthy (official true)
4 // truthy
"" // falsy (truthy if non-empty)
{} // falsy (truthy if contains fields or code ran)
[] // falsy (truthy if non-empty)
-0 // falsy / null / undefined equivalent-ish in Hi's model
// Equality is simple:
== // equivalent to JS ===
\`\`\`
## Conditionals
Blocks return their last value (like Rust expressions).
\`\`\`js
// if
(cond) ? { _("yes") }
// if / else
(cond) ? { _("yes") } : { _("no") }
// as an expression
tern: (cond) ? { "A" } : { "B" }
_(tern) // prints A or B
// else-if chain
score: 85
grade: (score > 90) ? { "A" }
: (score > 80) ? { "B" }
: { "C" }
_(grade) // "B"
\`\`\`
## Arrays
\`\`\`js
primes: [2, 3, 5, 7]
// Access
firstPrime: primes[0]
// Mutation
primes[0] = 1
_(primes) // [1, 3, 5, 7]
\`\`\`
## Loops
Use \`* { ... }\` after a loop header. \`>>\` continues, \`^\` breaks.
\`\`\`js
// for
(i: 0; i < 5; i = i + 1) * {
_(i)
(i == 2) ? { >> } // continue at i==2 (skip rest)
}
// while
(done: !0) * {
// ...
^ // break
}
\`\`\`
## Putting it together
Here are a few small programs that connect the dots.
### FizzBuzz
\`\`\`js
fizzbuzz: (n) {
(n % 15 == 0) ? { "FizzBuzz" }
: (n % 3 == 0) ? { "Fizz" }
: (n % 5 == 0) ? { "Buzz" }
: { n }
}
(i: 1; i <= 16; i = i + 1) * {
_( fizzbuzz(i) )
}
\`\`\`
### A tiny accumulator
\`\`\`js
acc: {
#sum: 0
add: (x) { sum = sum + x }
get: { sum }
}
acc.add(6)
acc.add(7)
_( acc.get() ) // 13
\`\`\`
### Recursive example
\`\`\`js
fib: (n) {
(n < 2) ? { n } : { fib(n-1) + fib(n-2) }
}
_( fib(6) ) // 8
\`\`\`
## Design philosophy
Hi keeps its core "mostly symbols" to be friendlier across languages and writing systems. Keywords appear where theyre practical; everywhere else, symbols do the heavy lifting. The goal: an approachable, expressive language for the whole world.
Hi is in active development. Expect changes, clarifications, and more features, examples, and tooling soon. Stay tuned!
`
// Render docs
const root = $('#suneHtml')
root.innerHTML = md.render(DOC)
// Add ids to headings + build TOC
const heads = $$('h1, h2, h3', root) // query relative later
const tocEl = $('#toc')
const items=[]
$$('#suneHtml h1, #suneHtml h2, #suneHtml h3').forEach(h=>{
const level = +h.tagName.slice(1)
const id = h.id || slug(h.textContent||'')
h.id = id
// small anchor
const a=document.createElement('a')
a.href = '#'+id
a.className='ml-2 align-middle text-gray-300 hover:text-gray-500'
a.innerHTML = '<svg data-lucide="link-2" class="inline h-3.5 w-3.5"></svg>'
h.appendChild(a)
if(level<=3 && level>=1){
items.push({id,level,text:h.textContent.replace(/\s*$/,'')})
}
})
tocEl.innerHTML = [
'<div class="px-3 py-2 text-xs font-medium text-gray-500 uppercase tracking-wide">Contents</div>',
...items.map(x=>{
const pad = x.level===1?'pl-3':x.level===2?'pl-5':'pl-8'
return `<a class="toc-link ${pad}" href="#${x.id}" data-id="${x.id}">${x.text}</a>`
})
].join('')
// Scroll handling
tocEl.addEventListener('click',e=>{
const a=e.target.closest('a[href^="#"]'); if(!a) return
e.preventDefault()
const id=a.getAttribute('href').slice(1)
const el=document.getElementById(id); if(!el) return
window.scrollTo({top:el.getBoundingClientRect().top+window.scrollY-72,behavior:'smooth'})
const overlay=$("#sidebarOverlay"); if(getComputedStyle(overlay).display!=='none') document.body.__x?.$data.open=false
})
// Scrollspy
const links = $$('#toc .toc-link')
const map = Object.fromEntries(links.map(a=>[a.dataset.id,a]))
const io = new IntersectionObserver(es=>{
es.forEach(({isIntersecting, target})=>{
if(!isIntersecting) return
const id=target.id; if(map[id]){
links.forEach(l=>l.classList.remove('toc-active'))
map[id].classList.add('toc-active')
}
})
},{rootMargin:'-60px 0px -70% 0px',threshold:1e-3})
$$('#suneHtml h1, #suneHtml h2').forEach(h=>io.observe(h))
// Highlight + small copy buttons
hljs.highlightAll()
$$('#suneHtml pre>code').forEach(code=>{
const pre=code.parentElement
pre.style.position='relative'
const btn=document.createElement('button')
btn.className='copy-btn'
btn.innerHTML='<svg data-lucide="copy" class="inline h-3.5 w-3.5 mr-1 -mt-0.5"></svg>Copy'
btn.addEventListener('click',async e=>{
e.stopPropagation()
try{
await navigator.clipboard.writeText(code.innerText)
btn.innerHTML='<svg data-lucide="check" class="inline h-3.5 w-3.5 mr-1 -mt-0.5"></svg>Copied'
icons(); setTimeout(()=>{btn.innerHTML='<svg data-lucide="copy" class="inline h-3.5 w-3.5 mr-1 -mt-0.5"></svg>Copy'; icons()},1200)
}catch{}
})
pre.appendChild(btn)
})
// Sidebar toggle (Alpine) for mobile
const side=$("#sidebarLeft"), overlay=$("#sidebarOverlay")
const open=()=>{side.classList.remove('-translate-x-full');overlay.classList.remove('hidden')}
const close=()=>{side.classList.add('-translate-x-full');overlay.classList.add('hidden')}
document.querySelector('button[title="Menu"]').addEventListener('click',open,{passive:true})
overlay.addEventListener('click',close,{passive:true})
// Icons
icons()
})
</script>
</body>
</html>