General documentation improvements
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
target/
|
*target*
|
||||||
*.pem
|
*.pem
|
||||||
gurty.toml
|
gurty.toml
|
||||||
certs
|
certs
|
||||||
search_indexes
|
search_indexes
|
||||||
config.toml
|
config.toml
|
||||||
|
.vscode
|
||||||
57
README.md
57
README.md
@@ -1,35 +1,36 @@
|
|||||||
gurted (verb)
|
# Gurted
|
||||||
|
|
||||||
|
Gurted (verb)
|
||||||
> “to do something smart, but also dangerous”
|
> “to do something smart, but also dangerous”
|
||||||
|
|
||||||
wayfinder (noun)
|
Wayfinder (noun)
|
||||||
> “a person helping others navigate”
|
> “a person helping others navigate”
|
||||||
|
|
||||||
In traditional web, you might be familiar with the term "browser." A **wayfinder** is that, but for the **GURT** protocol.
|
Gurted is an ecosystem similar to the World Wide Web, it features:
|
||||||
|
- It's own **Viewfinder** (a custom browser named Flumi) written in Rust and GDScript with [Godot](https://godotengine.org/),
|
||||||
|
- A custom HTML, CSS and ***Lua*** engine (We do **not like javascript**)
|
||||||
|
- A custom **DNS** that allows users to create domains with TLDs such as `.based`, `.delulu`, `.aura`, `.twin` and many more.
|
||||||
|
- A search engine called **Ringle**.
|
||||||
|
|
||||||
TODO:
|

|
||||||
1. Write a new **selection system**. Godot's built-in `RichTextLabel` selection is limited by the node's boundaries. In normal web, holding click and dragging your mouse across the screen will select text across multiple nodes. Godot doesn't have a "set_selected_text" property, despite having one for "get_selected_text".
|
|
||||||
2. **Right-click Dropdown** for basic text operations (Copy, Paste, Cut). Download options for images
|
|
||||||
3. **Select-all CTRL+A shortcut**
|
|
||||||
4. **Scrolling** in the website container
|
|
||||||
5. **Store** tab containers so switching tabs won't erase previous tab.
|
|
||||||
6. **GIF** support
|
|
||||||
7. **Video** support via [GDE GoZen](https://github.com/VoylinsGamedevJourney/gde_gozen)
|
|
||||||
8. **Required** attribute for inputs
|
|
||||||
9. Installer should register **gurt://** as a valid protocol thru the registry.
|
|
||||||
10. < input type=**datetime** />, essentially a type "date" but with a vertical separator, then `mm | ss | FORMAT` layout for time.
|
|
||||||
11. **< table >** component. [🔗 Related Godot proposal](https://github.com/godotengine/godot-proposals/issues/97)
|
|
||||||
12. **< canvas >** component should be theoretically impossible by exposing Godot `_draw()` APIs to Lua.
|
|
||||||
13. `grid` display property for CSS, using `GridContainer` in Godot.
|
|
||||||
14. Position `absolute` can be achieved by wrapping inside a Control node with a 0,0 size (so it's ignored by the layout), and moving the inner child according to CSS.
|
|
||||||
|
|
||||||
Issues:
|
# File structure
|
||||||
1. **< br />** counts as 1 element in **WebsiteContainer**, therefore despite being (0,0) in size, it counts as double in spacing
|
- `/dns` - The source code for the **DNS** (Domain Name System)
|
||||||
2. **Tween** API doesn't modify CSS, it operates independently at Godot level.
|
- `/docs` - The source code for the **Documentation page** available at https://docs.gurted.com
|
||||||
3. Certain properties like `scale` and `rotate` don't apply to the `active` pseudo-class because they rely on mouse_enter and mouse_exit events
|
- `/flumi` - The source code for the **Wayfinder** Flumi, used to view gurt:// sites
|
||||||
4. `<div style="bg-[#3b82f6] w-[100px] h-[100px] flex hover:scale-110 transition hover:rotate-45">Box</div>` something like this has the "Box" text (presumably the PanelContainer) as the target of the hover, not the div itself (which has the w/h size)
|
- `/protocol` - Source code for all gurt related things, like the gdextension and the rust library
|
||||||
5. font in button doesn't comply with CSS, its the projects default
|
- `/search-engine` - The Source code for the official **search engine** (Ringle)
|
||||||
6. The HTML, or Lua, doesn't support non-UTF8 characters. The HTML parser relies on `PackedByteArray` (for the XMLParser, from my memory).
|
|
||||||
|
|
||||||
Notes:
|
# Download and install
|
||||||
- **< input />** is sort-of inline in normal web. We render it as a block element (new-line).
|
|
||||||
- Fonts use **Flash of Unstyled Text (FOUT)** as opposed to **Flash of Invisible Text (FOIT)**, meaning the text with custom fonts will render with a generic font (sans-serif) while the custom ones downloads.
|
## Windows
|
||||||
|
Grab the installer from the [releases page](https://github.com/outpoot/gurted/releases) and run it as an administrator or download the binary and run it
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
Download the binary from [releases page](https://github.com/outpoot/gurted/releases) and run it.
|
||||||
|
|
||||||
|
## MacOS
|
||||||
|
Download the binary from the [releases page](https://github.com/outpoot/gurted/releases) and copy it to your applications folder.
|
||||||
|
|
||||||
|
# Compiling
|
||||||
|
The process is identycal to compiling a godot game, however if you modified the protocol library or the gdextension you have to rebuild the gurted gdextension library by running build.sh in `/protocol/gdextension` and copy `/protocol/gdextension/target/x86_64-unknown-linux-gnu/release/libgurt_godot.so` (or the windows/macos library) to `flumi/addons/gurt-protocol/bin/linux` or `flumi/addons/gurt-protocol/bin/windows` for windows.
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
# Domain Management API
|
# Domain Name System
|
||||||
|
|
||||||
This is a Domain Management API built with Rust (Actix Web) and PostgreSQL. It provides user authentication, domain registration with Discord approval workflow, and invite-based registration limits.
|
The gurted DNS is built with Rust (Actix Web) and PostgreSQL. It provides endpoints to create, read, update, and delete domain information, along with rate limiting for certain operations.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 🔐 **JWT Authentication** - Secure user registration and login
|
|
||||||
- 📝 **Domain Registration** - Submit domains for approval with usage limits
|
|
||||||
- 🤖 **Discord Integration** - Automatic approval workflow via Discord bot
|
|
||||||
- 📧 **Invite System** - Users can share registration slots via invite codes
|
|
||||||
- 🛡️ **Rate Limiting** - Protection against abuse
|
|
||||||
- 📊 **PostgreSQL Database** - Reliable data storage with migrations
|
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
@@ -19,24 +10,24 @@ This is a Domain Management API built with Rust (Actix Web) and PostgreSQL. It p
|
|||||||
- [GET /auth/me](#get-authme)
|
- [GET /auth/me](#get-authme)
|
||||||
- [POST /auth/invite](#post-authinvite)
|
- [POST /auth/invite](#post-authinvite)
|
||||||
- [POST /auth/redeem-invite](#post-authredeem-invite)
|
- [POST /auth/redeem-invite](#post-authredeem-invite)
|
||||||
- [GET /auth/domains](#get-authdomains) 🔒
|
- [GET /auth/domains](#get-authdomains) *
|
||||||
- [Domain Endpoints](#domain-endpoints)
|
- [Domain Endpoints](#domain-endpoints)
|
||||||
- [GET /](#get-)
|
- [GET /](#get-)
|
||||||
- [POST /domain](#post-domain) 🔒
|
- [POST /domain](#post-domain) *
|
||||||
- [GET /domain/:name/:tld](#get-domainnametld)
|
- [GET /domain/:name/:tld](#get-domainnametld)
|
||||||
- [PUT /domain/:name/:tld](#put-domainnametld) 🔒
|
- [PUT /domain/:name/:tld](#put-domainnametld) *
|
||||||
- [DELETE /domain/:name/:tld](#delete-domainnametld) 🔒
|
- [DELETE /domain/:name/:tld](#delete-domainnametld) *
|
||||||
- [GET /domains](#get-domains)
|
- [GET /domains](#get-domains)
|
||||||
- [GET /tlds](#get-tlds)
|
- [GET /tlds](#get-tlds)
|
||||||
- [POST /domain/check](#post-domaincheck)
|
- [POST /domain/check](#post-domaincheck)
|
||||||
|
|
||||||
🔒 = Requires authentication
|
* = Requires authentication
|
||||||
|
|
||||||
## Authentication Endpoints
|
## Authentication Endpoints
|
||||||
|
|
||||||
### POST /auth/register
|
### POST /auth/register
|
||||||
|
|
||||||
Register a new user account. New users start with 3 domain registrations.
|
Register a new user account. New users have a limit of 3 domain registrations by default.
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
@@ -84,7 +75,7 @@ Login with existing credentials.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /auth/me 🔒
|
### GET /auth/me *
|
||||||
|
|
||||||
Get current user information. Requires `Authorization: Bearer <token>` header.
|
Get current user information. Requires `Authorization: Bearer <token>` header.
|
||||||
|
|
||||||
@@ -98,9 +89,9 @@ Get current user information. Requires `Authorization: Bearer <token>` header.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /auth/invite 🔒
|
### POST /auth/invite *
|
||||||
|
|
||||||
Create an invite code that can be redeemed for 3 additional domain registrations. Requires authentication but does NOT consume any of your registrations.
|
Create an invite code that can be redeemed for 3 additional domain registrations. Requires authentication but does NOT consume any of the registrations of the inviting user.
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
```json
|
```json
|
||||||
@@ -109,7 +100,7 @@ Create an invite code that can be redeemed for 3 additional domain registrations
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /auth/redeem-invite 🔒
|
### POST /auth/redeem-invite *
|
||||||
|
|
||||||
Redeem an invite code to get 3 additional domain registrations. Requires authentication.
|
Redeem an invite code to get 3 additional domain registrations. Requires authentication.
|
||||||
|
|
||||||
@@ -128,9 +119,9 @@ Redeem an invite code to get 3 additional domain registrations. Requires authent
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /auth/domains 🔒
|
### GET /auth/domains *
|
||||||
|
|
||||||
Get all domains owned by the authenticated user, including their status. Requires `Authorization: Bearer <token>` header.
|
Get all domains owned by the authenticated user, including their status. Requires authentication.
|
||||||
|
|
||||||
**Query Parameters:**
|
**Query Parameters:**
|
||||||
- `page` - Page number (default: 1)
|
- `page` - Page number (default: 1)
|
||||||
@@ -191,9 +182,9 @@ GET /tlds.
|
|||||||
Ratelimits are as follows: 10 requests per 60s.
|
Ratelimits are as follows: 10 requests per 60s.
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /domain 🔒
|
### POST /domain *
|
||||||
|
|
||||||
Submit a domain for approval. Requires authentication and consumes one registration slot. The domain will be sent to Discord for manual approval.
|
Submit a domain for approval. Requires authentication and consumes one registration slot. The request will be sent to the moderators via discord for verification.
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
@@ -222,9 +213,9 @@ Fetch an approved domain by name and TLD. Only returns domains with 'approved' s
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT /domain/:name/:tld 🔒
|
### PUT /domain/:name/:tld *
|
||||||
|
|
||||||
Update the IP address of your approved domain. You can only update domains you own.
|
Update the IP address of the user's approved domain.
|
||||||
|
|
||||||
**Request:**
|
**Request:**
|
||||||
```json
|
```json
|
||||||
@@ -240,17 +231,17 @@ Update the IP address of your approved domain. You can only update domains you o
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE /domain/:name/:tld 🔒
|
### DELETE /domain/:name/:tld *
|
||||||
|
|
||||||
Delete your domain. You can only delete domains you own.
|
Delete a domain owned by the account.
|
||||||
|
|
||||||
**Response:**
|
**Response:**
|
||||||
- `200 OK` - Domain deleted successfully
|
- `200 OK` - Domain deleted successfully
|
||||||
- `404 Not Found` - Domain not found or not owned by you
|
- `404 Not Found` - Domain not found or not owned by the requesting account
|
||||||
|
|
||||||
### GET /domains
|
### GET /domains
|
||||||
|
|
||||||
Fetch all approved domains with pagination support. Only shows domains with 'approved' status.
|
Fetch all approved domains with pagination support.
|
||||||
|
|
||||||
**Query Parameters:**
|
**Query Parameters:**
|
||||||
- `page` (or `p`) - Page number (default: 1)
|
- `page` (or `p`) - Page number (default: 1)
|
||||||
@@ -304,11 +295,11 @@ Check if domain name(s) are available.
|
|||||||
|
|
||||||
## Discord Integration
|
## Discord Integration
|
||||||
|
|
||||||
When a user submits a domain registration, it's automatically sent to a configured Discord channel with:
|
When a user submits a domain registration, it's automatically sent to the configured Discord channel with:
|
||||||
|
|
||||||
- 📝 Domain details (name, TLD, IP, user info)
|
- 📝 Domain details (name, TLD, IP, user info)
|
||||||
- ✅ **Approve** button - Marks domain as approved
|
- ✅ **Approve** button - Marks domain as approved
|
||||||
- ❌ **Deny** button - Opens modal asking for denial reason
|
- ❌ **Deny** button - Opens a modal for inputing a denial reason
|
||||||
|
|
||||||
Discord admins can approve or deny registrations directly from Discord.
|
Discord admins can approve or deny registrations directly from Discord.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Website
|
# Gurted documentation
|
||||||
|
|
||||||
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
The documentation is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
---
|
|
||||||
slug: first-blog-post
|
|
||||||
title: First Blog Post
|
|
||||||
authors: [slorber, yangshun]
|
|
||||||
tags: [hola, docusaurus]
|
|
||||||
---
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet...
|
|
||||||
|
|
||||||
<!-- truncate -->
|
|
||||||
|
|
||||||
...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
---
|
|
||||||
slug: long-blog-post
|
|
||||||
title: Long Blog Post
|
|
||||||
authors: yangshun
|
|
||||||
tags: [hello, docusaurus]
|
|
||||||
---
|
|
||||||
|
|
||||||
This is the summary of a very long blog post,
|
|
||||||
|
|
||||||
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
|
|
||||||
|
|
||||||
<!-- truncate -->
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
slug: mdx-blog-post
|
|
||||||
title: MDX Blog Post
|
|
||||||
authors: [slorber]
|
|
||||||
tags: [docusaurus]
|
|
||||||
---
|
|
||||||
|
|
||||||
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
|
|
||||||
|
|
||||||
:::tip
|
|
||||||
|
|
||||||
Use the power of React to create interactive blog posts.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
{/* truncate */}
|
|
||||||
|
|
||||||
For example, use JSX to create an interactive button:
|
|
||||||
|
|
||||||
```js
|
|
||||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
|
||||||
```
|
|
||||||
|
|
||||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB |
@@ -1,34 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://cni08ewpfei11"
|
|
||||||
path="res://.godot/imported/docusaurus-plushie-banner.jpeg-fc2973b97ba5e43ee011baee4b7e1018.ctex"
|
|
||||||
metadata={
|
|
||||||
"vram_texture": false
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://docs/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg"
|
|
||||||
dest_files=["res://.godot/imported/docusaurus-plushie-banner.jpeg-fc2973b97ba5e43ee011baee4b7e1018.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=0
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/hdr_compression=1
|
|
||||||
compress/normal_map=0
|
|
||||||
compress/channel_pack=0
|
|
||||||
mipmaps/generate=false
|
|
||||||
mipmaps/limit=-1
|
|
||||||
roughness/mode=0
|
|
||||||
roughness/src_normal=""
|
|
||||||
process/fix_alpha_border=true
|
|
||||||
process/premult_alpha=false
|
|
||||||
process/normal_map_invert_y=false
|
|
||||||
process/hdr_as_srgb=false
|
|
||||||
process/hdr_clamp_exposure=false
|
|
||||||
process/size_limit=0
|
|
||||||
detect_3d/compress_to=1
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
slug: welcome
|
|
||||||
title: Welcome
|
|
||||||
authors: [slorber, yangshun]
|
|
||||||
tags: [facebook, hello, docusaurus]
|
|
||||||
---
|
|
||||||
|
|
||||||
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
|
|
||||||
|
|
||||||
Here are a few tips you might find useful.
|
|
||||||
|
|
||||||
<!-- truncate -->
|
|
||||||
|
|
||||||
Simply add Markdown files (or folders) to the `blog` directory.
|
|
||||||
|
|
||||||
Regular blog authors can be added to `authors.yml`.
|
|
||||||
|
|
||||||
The blog post date can be extracted from filenames, such as:
|
|
||||||
|
|
||||||
- `2019-05-30-welcome.md`
|
|
||||||
- `2019-05-30-welcome/index.md`
|
|
||||||
|
|
||||||
A blog post folder can be convenient to co-locate blog post images:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The blog supports tags as well!
|
|
||||||
|
|
||||||
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
yangshun:
|
|
||||||
name: Yangshun Tay
|
|
||||||
title: Ex-Meta Staff Engineer, Co-founder GreatFrontEnd
|
|
||||||
url: https://linkedin.com/in/yangshun
|
|
||||||
image_url: https://github.com/yangshun.png
|
|
||||||
page: true
|
|
||||||
socials:
|
|
||||||
x: yangshunz
|
|
||||||
linkedin: yangshun
|
|
||||||
github: yangshun
|
|
||||||
newsletter: https://www.greatfrontend.com
|
|
||||||
|
|
||||||
slorber:
|
|
||||||
name: Sébastien Lorber
|
|
||||||
title: Docusaurus maintainer
|
|
||||||
url: https://sebastienlorber.com
|
|
||||||
image_url: https://github.com/slorber.png
|
|
||||||
page:
|
|
||||||
# customize the url of the author page at /blog/authors/<permalink>
|
|
||||||
permalink: '/all-sebastien-lorber-articles'
|
|
||||||
socials:
|
|
||||||
x: sebastienlorber
|
|
||||||
linkedin: sebastienlorber
|
|
||||||
github: slorber
|
|
||||||
newsletter: https://thisweekinreact.com
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
facebook:
|
|
||||||
label: Facebook
|
|
||||||
permalink: /facebook
|
|
||||||
description: Facebook tag description
|
|
||||||
|
|
||||||
hello:
|
|
||||||
label: Hello
|
|
||||||
permalink: /hello
|
|
||||||
description: Hello tag description
|
|
||||||
|
|
||||||
docusaurus:
|
|
||||||
label: Docusaurus
|
|
||||||
permalink: /docusaurus
|
|
||||||
description: Docusaurus tag description
|
|
||||||
|
|
||||||
hola:
|
|
||||||
label: Hola
|
|
||||||
permalink: /hola
|
|
||||||
description: Hola tag description
|
|
||||||
2757
docs/bun.lock
Normal file
2757
docs/bun.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -458,5 +458,14 @@ Gurted provides sensible defaults for all HTML elements:
|
|||||||
- **Code**: `text-xl font-mono`
|
- **Code**: `text-xl font-mono`
|
||||||
- **Mark**: `bg-[#FFFF00]` (yellow highlight)
|
- **Mark**: `bg-[#FFFF00]` (yellow highlight)
|
||||||
- **Small**: `text-xl` (smaller than base)
|
- **Small**: `text-xl` (smaller than base)
|
||||||
|
- **Pre**: `text-xl font-mono`
|
||||||
And more.
|
- **B**: `font-bold`
|
||||||
|
- **I**: `font-italic`
|
||||||
|
- **U**: `underline`
|
||||||
|
- **Images**: `object-fill`
|
||||||
|
- **Select**: `text-[16px] bg-[#1b1b1b] rounded-md text-white hover:bg-[#2a2a2a] active:bg-[#101010] px-3 py-1.5`
|
||||||
|
- **Color input**: `w-32`
|
||||||
|
- **Range input**: Same as above
|
||||||
|
- **Text input**: `text-[16px] w-64`
|
||||||
|
- **Number input**: `w-32 text-[16px] bg-transparent border border-[#000000] rounded-[3px] text-[#000000] hover:border-[3px] hover:border-[#000000] px-3 py-1.5`
|
||||||
|
- **Date input**: `w-28 text-[16px] bg-[#1b1b1b] rounded-md text-white hover:bg-[#2a2a2a] active:bg-[#101010] px-3 py-1.5`
|
||||||
|
|||||||
1267
docs/docs/lua.md
1267
docs/docs/lua.md
File diff suppressed because it is too large
Load Diff
21
docs/docs/lua/audio.md
Normal file
21
docs/docs/lua/audio.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Audio API
|
||||||
|
|
||||||
|
Work with audio elements for sound playback.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local audio = gurt.select('#my-audio')
|
||||||
|
|
||||||
|
audio:play() -- Start playback
|
||||||
|
audio:pause() -- Pause playback
|
||||||
|
audio:stop() -- Stop and reset
|
||||||
|
|
||||||
|
audio.currentTime = 30.0 -- Seek to 30 seconds
|
||||||
|
audio.volume = 0.8 -- Set volume (0.0 - 1.0)
|
||||||
|
audio.loop = true -- Enable looping
|
||||||
|
audio.src = 'gurt://new-audio.mp3' -- Change source
|
||||||
|
|
||||||
|
local duration = audio.duration
|
||||||
|
local currentPos = audio.currentTime
|
||||||
|
local isPlaying = audio.playing
|
||||||
|
local isPaused = audio.paused
|
||||||
|
```
|
||||||
235
docs/docs/lua/canvas.md
Normal file
235
docs/docs/lua/canvas.md
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
# Canvas API
|
||||||
|
|
||||||
|
Gurted features a 2D canvas API similar to HTML5 Canvas, plus shader support.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local canvas = gurt.select('#my-canvas')
|
||||||
|
|
||||||
|
local ctx = canvas:withContext('2d')
|
||||||
|
local shaderCtx = canvas:withContext('shader')
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2D Drawing Context
|
||||||
|
|
||||||
|
### Rectangle
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Fill a solid rectangle
|
||||||
|
ctx:fillRect(x, y, width, height, color)
|
||||||
|
ctx:fillRect(50, 50, 100, 75, '#ff0000') -- Red filled rectangle
|
||||||
|
|
||||||
|
-- Draw rectangle outline
|
||||||
|
ctx:strokeRect(x, y, width, height, color, strokeWidth)
|
||||||
|
ctx:strokeRect(200, 50, 100, 75, '#00ff00', 3) -- Green outline, 3px thick
|
||||||
|
|
||||||
|
-- Clear a rectangular area
|
||||||
|
ctx:clearRect(x, y, width, height)
|
||||||
|
ctx:clearRect(80, 80, 40, 40) -- Clear 40x40 area
|
||||||
|
```
|
||||||
|
|
||||||
|
### Circle
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Draw filled or outlined circles
|
||||||
|
ctx:drawCircle(x, y, radius, color, filled)
|
||||||
|
ctx:drawCircle(150, 100, 30, '#0000ff', true) -- Filled blue circle
|
||||||
|
ctx:drawCircle(200, 100, 30, '#ff00ff', false) -- Outlined magenta circle
|
||||||
|
```
|
||||||
|
|
||||||
|
### Text
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ctx:drawText(x, y, text, color)
|
||||||
|
ctx:drawText(20, 250, 'Hello Canvas!', '#ffffff')
|
||||||
|
ctx:drawText(20, 280, 'Default Font Only', '#ffff00')
|
||||||
|
|
||||||
|
-- Font size can be set with setFont (size only, not family)
|
||||||
|
ctx:setFont('20px sans-serif') -- Only size matters
|
||||||
|
ctx:drawText(20, 300, 'Larger text', '#00ff00')
|
||||||
|
|
||||||
|
local metrics = ctx:measureText('Sample Text')
|
||||||
|
local textWidth = metrics.width
|
||||||
|
```
|
||||||
|
|
||||||
|
### Path-Based Drawing
|
||||||
|
|
||||||
|
For complex shapes, use path-based drawing methods:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ctx:beginPath()
|
||||||
|
|
||||||
|
-- Move to starting point without drawing
|
||||||
|
ctx:moveTo(100, 100)
|
||||||
|
|
||||||
|
-- Draw line to point
|
||||||
|
ctx:lineTo(200, 150)
|
||||||
|
ctx:lineTo(150, 200)
|
||||||
|
ctx:lineTo(50, 200)
|
||||||
|
|
||||||
|
-- Close the path (connects back to start)
|
||||||
|
ctx:closePath()
|
||||||
|
|
||||||
|
-- Draw the path
|
||||||
|
ctx:stroke() -- Draw outline
|
||||||
|
-- or
|
||||||
|
ctx:fill() -- Fill the shape
|
||||||
|
```
|
||||||
|
|
||||||
|
### Advanced Path Methods
|
||||||
|
|
||||||
|
#### Arc and Circle Paths
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Draw arc (part of circle)
|
||||||
|
ctx:arc(x, y, radius, startAngle, endAngle, counterclockwise)
|
||||||
|
|
||||||
|
-- Example: Draw a quarter circle
|
||||||
|
ctx:beginPath()
|
||||||
|
ctx:arc(200, 200, 50, 0, math.pi/2, false) -- 0 to 90 degrees
|
||||||
|
ctx:stroke()
|
||||||
|
|
||||||
|
-- Full circle path
|
||||||
|
ctx:beginPath()
|
||||||
|
ctx:arc(300, 200, 40, 0, 2 * math.pi, false) -- 0 to 360 degrees
|
||||||
|
ctx:fill()
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Curve Methods
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Quadratic curve (one control point)
|
||||||
|
ctx:quadraticCurveTo(controlX, controlY, endX, endY)
|
||||||
|
|
||||||
|
-- Example: Smooth curve
|
||||||
|
ctx:beginPath()
|
||||||
|
ctx:moveTo(50, 300)
|
||||||
|
ctx:quadraticCurveTo(150, 250, 250, 300) -- Control point at (150,250)
|
||||||
|
ctx:stroke()
|
||||||
|
|
||||||
|
-- Bezier curve (two control points)
|
||||||
|
ctx:bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY)
|
||||||
|
|
||||||
|
-- Example: S-curve
|
||||||
|
ctx:beginPath()
|
||||||
|
ctx:moveTo(50, 350)
|
||||||
|
ctx:bezierCurveTo(100, 300, 200, 400, 250, 350)
|
||||||
|
ctx:stroke()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Styling and Properties
|
||||||
|
|
||||||
|
### Setting Draw Styles
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Set stroke (outline) color
|
||||||
|
ctx:setStrokeStyle('#ff0000') -- Red outline
|
||||||
|
ctx:setStrokeStyle('rgba(255, 0, 0, 0.5)') -- Semi-transparent red
|
||||||
|
ctx:setStrokeStyle('red-500') -- Tailwind color names
|
||||||
|
ctx:setStrokeStyle('blue') -- Named colors
|
||||||
|
|
||||||
|
-- Set fill color
|
||||||
|
ctx:setFillStyle('#00ff00') -- Green fill
|
||||||
|
ctx:setFillStyle('#33aa88') -- Teal fill
|
||||||
|
ctx:setFillStyle('slate-800') -- Tailwind colors
|
||||||
|
ctx:setFillStyle('transparent') -- Named transparent
|
||||||
|
|
||||||
|
-- Set line width for strokes
|
||||||
|
ctx:setLineWidth(5) -- 5 pixel wide lines
|
||||||
|
ctx:setLineWidth(0.5) -- Thin lines
|
||||||
|
|
||||||
|
-- Set font for text (size only, not family)
|
||||||
|
ctx:setFont('20px sans-serif') -- Only size matters
|
||||||
|
ctx:setFont('16px Arial') -- Font family ignored
|
||||||
|
ctx:setFont('14px monospace') -- Uses default font at 14px
|
||||||
|
```
|
||||||
|
|
||||||
|
**Color Support**: Canvas color parsing is identical to CSS styling - supports hex colors (`#ff0000`), RGB/RGBA (`rgba(255,0,0,0.5)`), Tailwind color names (`red-500`, `slate-800`), and basic named colors (`red`, `blue`, `transparent`).
|
||||||
|
|
||||||
|
### Using Styles in Drawing
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Set up styles first
|
||||||
|
ctx:setFillStyle('#ff6b6b')
|
||||||
|
ctx:setStrokeStyle('#4ecdc4')
|
||||||
|
ctx:setLineWidth(3)
|
||||||
|
|
||||||
|
-- Then draw with those styles
|
||||||
|
ctx:fillRect(50, 50, 100, 100) -- Uses fill style
|
||||||
|
ctx:strokeRect(200, 50, 100, 100) -- Uses stroke style and line width
|
||||||
|
|
||||||
|
-- Styles persist until changed
|
||||||
|
ctx:setFillStyle('#45b7d1')
|
||||||
|
ctx:fillRect(50, 200, 100, 100) -- Now uses blue fill
|
||||||
|
```
|
||||||
|
|
||||||
|
## Transformations
|
||||||
|
|
||||||
|
Canvas transformations allow you to modify the coordinate system for drawing operations.
|
||||||
|
|
||||||
|
### Basic Transformations
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ctx:save()
|
||||||
|
ctx:translate(100, 50)
|
||||||
|
ctx:rotate(math.pi / 4)
|
||||||
|
ctx:scale(2.0, 1.5)
|
||||||
|
ctx:fillRect(0, 0, 50, 50)
|
||||||
|
ctx:restore()
|
||||||
|
ctx:fillRect(0, 0, 50, 50)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Transformation Examples
|
||||||
|
|
||||||
|
```lua
|
||||||
|
ctx:save()
|
||||||
|
ctx:translate(200, 200)
|
||||||
|
ctx:rotate(math.pi / 6)
|
||||||
|
ctx:drawText(-25, 0, 'Rotated', 'Arial', '#000000')
|
||||||
|
ctx:restore()
|
||||||
|
|
||||||
|
for i = 1, 5 do
|
||||||
|
ctx:save()
|
||||||
|
ctx:scale(i * 0.3, i * 0.3)
|
||||||
|
ctx:strokeRect(100, 100, 50, 50)
|
||||||
|
ctx:restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
for angle = 0, 360, 30 do
|
||||||
|
ctx:save()
|
||||||
|
ctx:translate(200, 200)
|
||||||
|
ctx:rotate(math.rad(angle))
|
||||||
|
ctx:fillRect(50, -5, 40, 10)
|
||||||
|
ctx:restore()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Shader Context
|
||||||
|
|
||||||
|
For advanced visual effects, use the shader context:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local canvas = gurt.select('#shader-canvas')
|
||||||
|
local shaderCtx = canvas:withContext('shader')
|
||||||
|
|
||||||
|
shaderCtx:source([[
|
||||||
|
shader_type canvas_item;
|
||||||
|
|
||||||
|
uniform float time : hint_range(0.0, 10.0) = 1.0;
|
||||||
|
uniform vec2 resolution;
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
vec2 uv = UV;
|
||||||
|
|
||||||
|
// Create animated rainbow effect
|
||||||
|
vec3 color = vec3(
|
||||||
|
0.5 + 0.5 * cos(time + uv.x * 6.0),
|
||||||
|
0.5 + 0.5 * cos(time + uv.y * 6.0 + 2.0),
|
||||||
|
0.5 + 0.5 * cos(time + (uv.x + uv.y) * 6.0 + 4.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
COLOR = vec4(color, 1.0);
|
||||||
|
}
|
||||||
|
]])
|
||||||
|
```
|
||||||
315
docs/docs/lua/elements.md
Normal file
315
docs/docs/lua/elements.md
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
# Elements
|
||||||
|
|
||||||
|
Elements returned by `gurt.select()`, `gurt.create()`, etc. have the following properties and methods:
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
### element.text
|
||||||
|
|
||||||
|
Gets or sets the text content of an element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local p = gurt.select('p')
|
||||||
|
p.text = 'New paragraph content'
|
||||||
|
local currentText = p.text
|
||||||
|
```
|
||||||
|
|
||||||
|
### element.value
|
||||||
|
|
||||||
|
Gets or sets the value of form elements.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local input = gurt.select('#username')
|
||||||
|
input.value = 'john_doe'
|
||||||
|
local username = input.value
|
||||||
|
|
||||||
|
local checkbox = gurt.select('#agree')
|
||||||
|
checkbox.value = true -- Check the checkbox
|
||||||
|
```
|
||||||
|
|
||||||
|
### element.visible
|
||||||
|
|
||||||
|
Gets or sets element visibility.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local modal = gurt.select('#modal')
|
||||||
|
modal.visible = false -- Hide element
|
||||||
|
modal.visible = true -- Show element
|
||||||
|
|
||||||
|
if modal.visible then
|
||||||
|
trace.log('Element is visible')
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### element.children
|
||||||
|
|
||||||
|
Gets an array of child elements.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local container = gurt.select('.container')
|
||||||
|
local children = container.children
|
||||||
|
|
||||||
|
for i = 1, #children do
|
||||||
|
local child = children[i]
|
||||||
|
trace.log('Child ' .. i .. ': ' .. child.text)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## DOM Traversal
|
||||||
|
|
||||||
|
### element.parent
|
||||||
|
|
||||||
|
Gets the parent element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local button = gurt.select('#my-button')
|
||||||
|
local container = button.parent
|
||||||
|
```
|
||||||
|
|
||||||
|
### element.nextSibling / element.previousSibling
|
||||||
|
|
||||||
|
Gets adjacent sibling elements.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local current = gurt.select('#current-item')
|
||||||
|
local next = current.nextSibling
|
||||||
|
local prev = current.previousSibling
|
||||||
|
```
|
||||||
|
|
||||||
|
### element.firstChild / element.lastChild
|
||||||
|
|
||||||
|
Gets first or last child element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local list = gurt.select('ul')
|
||||||
|
local firstItem = list.firstChild
|
||||||
|
local lastItem = list.lastChild
|
||||||
|
```
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### element:on(eventName, callback)
|
||||||
|
|
||||||
|
Adds an event listener. Returns a subscription object.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local button = gurt.select('#my-button')
|
||||||
|
|
||||||
|
-- Click event
|
||||||
|
local subscription = button:on('click', function()
|
||||||
|
trace.log('Button clicked!')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Mouse events
|
||||||
|
button:on('mouseenter', function()
|
||||||
|
button.classList:add('hover-effect')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mouseexit', function()
|
||||||
|
button.classList:remove('hover-effect')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Input events (for form elements)
|
||||||
|
local input = gurt.select('#username')
|
||||||
|
input:on('change', function(event)
|
||||||
|
trace.log('Input changed to: ' .. event.value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Focus events
|
||||||
|
input:on('focusin', function()
|
||||||
|
trace.log('Input focused')
|
||||||
|
end)
|
||||||
|
|
||||||
|
input:on('focusout', function()
|
||||||
|
trace.log('Input lost focus')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Unsubscribe from event
|
||||||
|
subscription:unsubscribe()
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:append(childElement)
|
||||||
|
|
||||||
|
Adds a child element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local container = gurt.select('.container')
|
||||||
|
local newDiv = gurt.create('div', { text = 'New content' })
|
||||||
|
container:append(newDiv)
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:remove()
|
||||||
|
|
||||||
|
Removes the element from the DOM.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local elementToRemove = gurt.select('#temporary')
|
||||||
|
elementToRemove:remove()
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:insertBefore(newElement, referenceElement)
|
||||||
|
|
||||||
|
Inserts an element before another element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local container = gurt.select('.container')
|
||||||
|
local newElement = gurt.create('div', { text = 'Inserted' })
|
||||||
|
local reference = gurt.select('#reference')
|
||||||
|
container:insertBefore(newElement, reference)
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:insertAfter(newElement, referenceElement)
|
||||||
|
|
||||||
|
Inserts an element after another element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local container = gurt.select('.container')
|
||||||
|
local newElement = gurt.create('div', { text = 'Inserted' })
|
||||||
|
local reference = gurt.select('#reference')
|
||||||
|
container:insertAfter(newElement, reference)
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:replace(oldElement, newElement)
|
||||||
|
|
||||||
|
Replaces a child element with a new element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local container = gurt.select('.container')
|
||||||
|
local oldElement = gurt.select('#old')
|
||||||
|
local newElement = gurt.create('div', { text = 'Replacement' })
|
||||||
|
container:replace(oldElement, newElement)
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:clone(deep)
|
||||||
|
|
||||||
|
Creates a copy of the element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Shallow clone (element only)
|
||||||
|
local copy = element:clone(false)
|
||||||
|
|
||||||
|
-- Deep clone (element and all children)
|
||||||
|
local deepCopy = element:clone(true)
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:getAttribute(name) / element:setAttribute(name, value)
|
||||||
|
|
||||||
|
Gets or sets element attributes.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local img = gurt.select('img')
|
||||||
|
local src = img:getAttribute('src')
|
||||||
|
img:setAttribute('alt', 'Description text')
|
||||||
|
|
||||||
|
-- Remove attribute by setting empty value
|
||||||
|
img:setAttribute('title', '')
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:show() / element:hide()
|
||||||
|
|
||||||
|
Shows or hides an element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local modal = gurt.select('#modal')
|
||||||
|
modal:show() -- Makes element visible
|
||||||
|
modal:hide() -- Hides element
|
||||||
|
```
|
||||||
|
|
||||||
|
### element:focus() / element:unfocus()
|
||||||
|
|
||||||
|
Sets or removes focus from an element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local input = gurt.select('#search')
|
||||||
|
input:focus() -- Focus the input
|
||||||
|
input:unfocus() -- Remove focus
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class List Management
|
||||||
|
|
||||||
|
### element.classList
|
||||||
|
|
||||||
|
Provides methods for managing CSS classes.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local button = gurt.select('#my-button')
|
||||||
|
|
||||||
|
-- Add classes
|
||||||
|
button.classList:add('active')
|
||||||
|
button.classList:add('btn-primary')
|
||||||
|
|
||||||
|
-- Remove classes
|
||||||
|
button.classList:remove('disabled')
|
||||||
|
|
||||||
|
-- Toggle classes
|
||||||
|
button.classList:toggle('selected')
|
||||||
|
|
||||||
|
-- Check if class exists
|
||||||
|
if button.classList:contains('active') then
|
||||||
|
trace.log('Button is active')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get specific class by index (1-based)
|
||||||
|
local firstClass = button.classList:item(1)
|
||||||
|
|
||||||
|
-- Get number of classes
|
||||||
|
local classCount = button.classList.length
|
||||||
|
```
|
||||||
|
|
||||||
|
## Animations
|
||||||
|
|
||||||
|
### element:createTween()
|
||||||
|
|
||||||
|
Creates a tween animation for the element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local box = gurt.select('#animated-box')
|
||||||
|
|
||||||
|
-- Fade out
|
||||||
|
box:createTween()
|
||||||
|
:to('opacity', 0)
|
||||||
|
:duration(1.0)
|
||||||
|
:easing('out')
|
||||||
|
:transition('linear')
|
||||||
|
:play()
|
||||||
|
|
||||||
|
-- Move and scale
|
||||||
|
box:createTween()
|
||||||
|
:to('x', 200)
|
||||||
|
:to('y', 100)
|
||||||
|
:to('scale', 1.5)
|
||||||
|
:duration(2.0)
|
||||||
|
:easing('inout')
|
||||||
|
:transition('cubic')
|
||||||
|
:play()
|
||||||
|
|
||||||
|
-- Color animation
|
||||||
|
box:createTween()
|
||||||
|
:to('backgroundColor', '#ff0000')
|
||||||
|
:duration(1.5)
|
||||||
|
:easing('out')
|
||||||
|
:transition('quad')
|
||||||
|
:play()
|
||||||
|
|
||||||
|
-- Rotation
|
||||||
|
box:createTween()
|
||||||
|
:to('rotation', 360)
|
||||||
|
:duration(3.0)
|
||||||
|
:easing('inout')
|
||||||
|
:transition('sine')
|
||||||
|
:play()
|
||||||
|
```
|
||||||
|
|
||||||
|
**Available Tween Properties:**
|
||||||
|
- `opacity` - Element transparency (0-1)
|
||||||
|
- `backgroundColor` - Background color (hex format)
|
||||||
|
- `scale` - Element scale (1.0 = normal size)
|
||||||
|
- `rotation` - Rotation in degrees
|
||||||
|
- `x`, `y` - Position offset
|
||||||
|
|
||||||
|
**Easing Types:** `'in'`, `'out'`, `'inout'`, `'outin'`
|
||||||
|
|
||||||
|
**Transition Types:** `'linear'`, `'quad'`, `'cubic'`, `'quart'`, `'quint'`, `'sine'`, `'expo'`, `'circ'`, `'elastic'`, `'back'`, `'bounce'`
|
||||||
|
|
||||||
|

|
||||||
|
Resource: [Reddit](https://www.reddit.com/r/godot/comments/frqzup/godot_tweening_cheat_sheet/)
|
||||||
126
docs/docs/lua/handling.md
Normal file
126
docs/docs/lua/handling.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# Handlers
|
||||||
|
|
||||||
|
## Event Handling
|
||||||
|
|
||||||
|
### Body Events
|
||||||
|
|
||||||
|
Global events that can be captured on the document body:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Keyboard events
|
||||||
|
gurt.body:on('keydown', function(event)
|
||||||
|
trace.log('Key down: ' .. event.key)
|
||||||
|
if event.ctrl and event.key == 's' then
|
||||||
|
trace.log('Ctrl+S pressed - Save shortcut!')
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
gurt.body:on('keyup', function(event)
|
||||||
|
trace.log('Key up: ' .. event.key)
|
||||||
|
end)
|
||||||
|
|
||||||
|
gurt.body:on('keypress', function(event)
|
||||||
|
trace.log('Key pressed: ' .. event.key)
|
||||||
|
-- Event properties: key, keycode, ctrl, shift, alt
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Mouse events
|
||||||
|
gurt.body:on('mousemove', function(event)
|
||||||
|
trace.log('Mouse at: ' .. event.x .. ', ' .. event.y)
|
||||||
|
-- Event properties: x, y, deltaX, deltaY
|
||||||
|
end)
|
||||||
|
|
||||||
|
gurt.body:on('mouseenter', function()
|
||||||
|
trace.log('Mouse entered page')
|
||||||
|
end)
|
||||||
|
|
||||||
|
gurt.body:on('mouseexit', function()
|
||||||
|
trace.log('Mouse left page')
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Element Events
|
||||||
|
|
||||||
|
Events specific to DOM elements:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local button = gurt.select('#my-button')
|
||||||
|
|
||||||
|
-- Mouse events
|
||||||
|
button:on('click', function()
|
||||||
|
trace.log('Button clicked!')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mousedown', function()
|
||||||
|
trace.log('Mouse button pressed')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mouseup', function()
|
||||||
|
trace.log('Mouse button released')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mouseenter', function()
|
||||||
|
trace.log('Mouse entered button')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mouseexit', function()
|
||||||
|
trace.log('Mouse left button')
|
||||||
|
end)
|
||||||
|
|
||||||
|
button:on('mousemove', function(event)
|
||||||
|
trace.log('Mouse moved over button: ' .. event.x .. ', ' .. event.y)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Focus events
|
||||||
|
local input = gurt.select('#text-input')
|
||||||
|
input:on('focusin', function()
|
||||||
|
trace.log('Input gained focus')
|
||||||
|
end)
|
||||||
|
|
||||||
|
input:on('focusout', function()
|
||||||
|
trace.log('Input lost focus')
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Form events
|
||||||
|
input:on('change', function(event)
|
||||||
|
trace.log('Input value changed to: ' .. event.value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
input:on('input', function(event)
|
||||||
|
trace.log('Input text: ' .. event.value)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- For file inputs
|
||||||
|
local fileInput = gurt.select('#file-input')
|
||||||
|
fileInput:on('change', function(event)
|
||||||
|
trace.log('File selected: ' .. event.fileName)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- For form submission
|
||||||
|
local form = gurt.select('#my-form')
|
||||||
|
form:on('submit', function(event)
|
||||||
|
trace.log('Form submitted with data:')
|
||||||
|
for key, value in pairs(event.data) do
|
||||||
|
trace.log(key .. ': ' .. tostring(value))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### pcall for Protected Calls
|
||||||
|
|
||||||
|
Use Lua's `pcall` for error handling:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local success, result = pcall(function()
|
||||||
|
local data = JSON.parse('invalid json')
|
||||||
|
return data
|
||||||
|
end)
|
||||||
|
|
||||||
|
if success then
|
||||||
|
trace.log('Parse successful: ' .. tostring(result))
|
||||||
|
else
|
||||||
|
trace.log('Parse failed: ' .. result) -- result contains error message
|
||||||
|
end
|
||||||
|
```
|
||||||
306
docs/docs/lua/intro.md
Normal file
306
docs/docs/lua/intro.md
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Intro
|
||||||
|
|
||||||
|
Gurted provides a Lua API that enables dynamic web development with client-side scripting. The Lua runtime is integrated into the browser engine and provides access to DOM manipulation, network requests, animations, and more.
|
||||||
|
|
||||||
|
## Global: gurt
|
||||||
|
|
||||||
|
The main global object for DOM manipulation and core functionality.
|
||||||
|
|
||||||
|
### gurt.select(selector)
|
||||||
|
|
||||||
|
Selects the first element matching the CSS selector.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local element = gurt.select('#my-id')
|
||||||
|
local firstButton = gurt.select('button')
|
||||||
|
local classElement = gurt.select('.my-class')
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.selectAll(selector)
|
||||||
|
|
||||||
|
Selects all elements matching the CSS selector, returns an array.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local allButtons = gurt.selectAll('button')
|
||||||
|
local listItems = gurt.selectAll('li')
|
||||||
|
|
||||||
|
-- Iterate through results
|
||||||
|
for i = 1, #allButtons do
|
||||||
|
local button = allButtons[i]
|
||||||
|
button.text = 'Button ' .. i
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.create(tagName, options)
|
||||||
|
|
||||||
|
Creates a new HTML element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Basic element
|
||||||
|
local div = gurt.create('div')
|
||||||
|
|
||||||
|
-- Element with attributes and content
|
||||||
|
local button = gurt.create('button', {
|
||||||
|
text = 'Click me!',
|
||||||
|
style = 'bg-blue-500 text-white px-4 py-2 rounded',
|
||||||
|
id = 'my-button'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.body
|
||||||
|
|
||||||
|
Reference to the document body element.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Add event listeners to body
|
||||||
|
gurt.body:on('keydown', function(event)
|
||||||
|
trace.log('Key pressed: ' .. event.key)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Append elements to body
|
||||||
|
local newDiv = gurt.create('div', { text = 'Hello World!' })
|
||||||
|
gurt.body:append(newDiv)
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.location
|
||||||
|
|
||||||
|
Browser location and navigation control.
|
||||||
|
|
||||||
|
### gurt.location.href
|
||||||
|
|
||||||
|
Gets the current URL.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local currentUrl = gurt.location.href
|
||||||
|
trace.log('Current URL: ' .. currentUrl)
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.location.reload()
|
||||||
|
|
||||||
|
Reloads the current page.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gurt.location.reload()
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.location.goto(url)
|
||||||
|
|
||||||
|
Navigates to a new URL.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
gurt.location.goto('gurt://example.com/page')
|
||||||
|
gurt.location.goto('https://external-site.com')
|
||||||
|
```
|
||||||
|
|
||||||
|
### gurt.location.query
|
||||||
|
|
||||||
|
Query parameter access.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Get a specific parameter
|
||||||
|
local userId = gurt.location.query.get('user_id')
|
||||||
|
|
||||||
|
-- Check if parameter exists
|
||||||
|
if gurt.location.query.has('debug') then
|
||||||
|
trace.log('Debug mode enabled')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get all values for a parameter (for repeated params)
|
||||||
|
local tags = gurt.location.query.getAll('tag')
|
||||||
|
```
|
||||||
|
## Global: trace
|
||||||
|
|
||||||
|
The global trace table for logging messages to the console.
|
||||||
|
|
||||||
|
### trace.log(message)
|
||||||
|
Identical to `print()`, logs a message to the console.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
trace.log('Hello from Lua!')
|
||||||
|
```
|
||||||
|
|
||||||
|
### trace.warn(message)
|
||||||
|
Logs a warning message to the console.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
trace.warn('This is a warning!')
|
||||||
|
```
|
||||||
|
|
||||||
|
### trace.error(message)
|
||||||
|
Logs an error message to the console.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
trace.error('This is an error!')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Time API
|
||||||
|
|
||||||
|
### Time.now()
|
||||||
|
|
||||||
|
Gets current Unix timestamp.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local timestamp = Time.now()
|
||||||
|
trace.log('Current time: ' .. timestamp)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time.format(timestamp, format)
|
||||||
|
|
||||||
|
Formats a timestamp using format strings.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local now = Time.now()
|
||||||
|
local formatted = Time.format(now, '%Y-%m-%d %H:%M:%S')
|
||||||
|
trace.log('Formatted: ' .. formatted)
|
||||||
|
|
||||||
|
-- Format strings
|
||||||
|
-- %Y - Full year (2024)
|
||||||
|
-- %y - Two-digit year (24)
|
||||||
|
-- %m - Month (01-12)
|
||||||
|
-- %d - Day (01-31)
|
||||||
|
-- %H - Hour 24-format (00-23)
|
||||||
|
-- %I - Hour 12-format (01-12)
|
||||||
|
-- %M - Minute (00-59)
|
||||||
|
-- %S - Second (00-59)
|
||||||
|
-- %p - AM/PM
|
||||||
|
-- %A - Full weekday name
|
||||||
|
-- %a - Abbreviated weekday name
|
||||||
|
-- %B - Full month name
|
||||||
|
-- %b - Abbreviated month name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time.date(timestamp)
|
||||||
|
|
||||||
|
Gets date components as a table.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local now = Time.now()
|
||||||
|
local date = Time.date(now)
|
||||||
|
|
||||||
|
trace.log('Year: ' .. date.year)
|
||||||
|
trace.log('Month: ' .. date.month)
|
||||||
|
trace.log('Day: ' .. date.day)
|
||||||
|
trace.log('Hour: ' .. date.hour)
|
||||||
|
trace.log('Minute: ' .. date.minute)
|
||||||
|
trace.log('Second: ' .. date.second)
|
||||||
|
trace.log('Weekday: ' .. date.weekday) -- 0=Sunday, 6=Saturday
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time.sleep(seconds)
|
||||||
|
|
||||||
|
Pauses execution for a specified duration.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
trace.log('Starting...')
|
||||||
|
Time.sleep(2.0) -- Wait 2 seconds
|
||||||
|
trace.log('Done waiting!')
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
This blocks the entire Lua thread. Use with caution, we recommend using `setTimeout()` for non-blocking delays.
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Time.benchmark(function)
|
||||||
|
|
||||||
|
Measures function execution time.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local elapsed, result = Time.benchmark(function()
|
||||||
|
-- Some complex calculation
|
||||||
|
local sum = 0
|
||||||
|
for i = 1, 1000000 do
|
||||||
|
sum = sum + i
|
||||||
|
end
|
||||||
|
return sum
|
||||||
|
end)
|
||||||
|
|
||||||
|
trace.log('Function took ' .. elapsed .. ' seconds')
|
||||||
|
trace.log('Result: ' .. result)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time.timer()
|
||||||
|
|
||||||
|
Creates a timer object for measuring intervals.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local timer = Time.timer()
|
||||||
|
|
||||||
|
-- Do some work...
|
||||||
|
Time.sleep(1.5)
|
||||||
|
|
||||||
|
local elapsed = timer:elapsed()
|
||||||
|
trace.log('Elapsed: ' .. elapsed .. ' seconds')
|
||||||
|
|
||||||
|
timer:reset() -- Reset timer
|
||||||
|
```
|
||||||
|
|
||||||
|
### Time.delay(seconds)
|
||||||
|
|
||||||
|
Creates a delay object for non-blocking waits.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local delay = Time.delay(3.0)
|
||||||
|
|
||||||
|
-- Check if delay is complete
|
||||||
|
if delay:complete() then
|
||||||
|
trace.log('Delay finished!')
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get remaining time
|
||||||
|
local remaining = delay:remaining()
|
||||||
|
trace.log('Time left: ' .. remaining .. ' seconds')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Timeout and Interval Functions
|
||||||
|
|
||||||
|
### setTimeout(callback, milliseconds)
|
||||||
|
|
||||||
|
Executes a function after a delay.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local timeoutId = setTimeout(function()
|
||||||
|
trace.log('This runs after 2 seconds')
|
||||||
|
end, 2000)
|
||||||
|
|
||||||
|
-- Cancel the timeout
|
||||||
|
-- clearTimeout(timeoutId)
|
||||||
|
```
|
||||||
|
|
||||||
|
### setInterval(callback, milliseconds)
|
||||||
|
|
||||||
|
Executes a function repeatedly at intervals.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local intervalId = setInterval(function()
|
||||||
|
trace.log('This runs every second')
|
||||||
|
end, 1000)
|
||||||
|
|
||||||
|
setTimeout(function()
|
||||||
|
clearInterval(intervalId)
|
||||||
|
trace.log('Interval stopped')
|
||||||
|
end, 5000)
|
||||||
|
```
|
||||||
|
|
||||||
|
### clearTimeout(timeoutId) / clearInterval(intervalId)
|
||||||
|
|
||||||
|
Cancels scheduled timeouts or intervals.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local id = setTimeout(function()
|
||||||
|
trace.log('This will not run')
|
||||||
|
end, 1000)
|
||||||
|
|
||||||
|
clearTimeout(id)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clipboard API
|
||||||
|
|
||||||
|
Write to the system clipboard.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
Clipboard.write('Hello clipboard!')
|
||||||
|
```
|
||||||
154
docs/docs/lua/network.md
Normal file
154
docs/docs/lua/network.md
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
# Network API
|
||||||
|
|
||||||
|
### Fetch
|
||||||
|
|
||||||
|
#### fetch(url, options)
|
||||||
|
|
||||||
|
Makes HTTP requests with full control over method, headers, and body.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Simple GET request
|
||||||
|
local response = fetch('https://api.example.com/data')
|
||||||
|
|
||||||
|
-- POST request with data
|
||||||
|
local response = fetch('https://api.example.com/users', {
|
||||||
|
method = 'POST',
|
||||||
|
headers = {
|
||||||
|
['Content-Type'] = 'application/json',
|
||||||
|
['Authorization'] = 'Bearer token123'
|
||||||
|
},
|
||||||
|
body = JSON.stringify({
|
||||||
|
name = 'John Doe',
|
||||||
|
email = 'john@example.com'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Check response
|
||||||
|
if response:ok() then
|
||||||
|
local data = response:json() -- Parse JSON response
|
||||||
|
local text = response:text() -- Get as text
|
||||||
|
|
||||||
|
trace.log('Status: ' .. response.status)
|
||||||
|
trace.log('Status Text: ' .. response.statusText)
|
||||||
|
|
||||||
|
-- Access headers
|
||||||
|
local contentType = response.headers['content-type']
|
||||||
|
else
|
||||||
|
trace.log('Request failed with status: ' .. response.status)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**Supported Methods:** `GET`, `POST`, `PUT`, `DELETE`, `HEAD`, `OPTIONS`, `PATCH`
|
||||||
|
|
||||||
|
**Relative URLs** are automatically resolved to the current domain with `gurt://` protocol.
|
||||||
|
|
||||||
|
## WebSocket API
|
||||||
|
|
||||||
|
Real-time communication with WebSocket servers.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local ws = WebSocket.new('ws://localhost:8080/chat')
|
||||||
|
|
||||||
|
ws:on('open', function()
|
||||||
|
trace.log('WebSocket connected')
|
||||||
|
ws:send('Hello server!')
|
||||||
|
end)
|
||||||
|
|
||||||
|
ws:on('message', function(data)
|
||||||
|
trace.log('Received: ' .. data)
|
||||||
|
end)
|
||||||
|
|
||||||
|
ws:on('close', function(code, reason)
|
||||||
|
trace.log('WebSocket closed: ' .. code .. ' - ' .. reason)
|
||||||
|
end)
|
||||||
|
|
||||||
|
ws:on('error', function(error)
|
||||||
|
trace.log('WebSocket error: ' .. error)
|
||||||
|
end)
|
||||||
|
|
||||||
|
ws:send('Hello from client!')
|
||||||
|
ws:send(JSON.stringify({ type = 'chat', message = 'Hello!' }))
|
||||||
|
|
||||||
|
ws:close()
|
||||||
|
|
||||||
|
if ws.readyState == WebSocket.OPEN then
|
||||||
|
ws:send('Connected message')
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
**WebSocket States:**
|
||||||
|
- `WebSocket.CONNECTING` (0) - Connection in progress
|
||||||
|
- `WebSocket.OPEN` (1) - Connection established
|
||||||
|
- `WebSocket.CLOSING` (2) - Connection closing
|
||||||
|
- `WebSocket.CLOSED` (3) - Connection closed
|
||||||
|
|
||||||
|
## URL API
|
||||||
|
|
||||||
|
URL encoding and decoding utilities for handling special characters in URLs.
|
||||||
|
|
||||||
|
### urlEncode(string)
|
||||||
|
|
||||||
|
Encodes a string for safe use in URLs by converting special characters to percent-encoded format.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local encoded = urlEncode('hello world!')
|
||||||
|
trace.log(encoded) -- hello%20world%21
|
||||||
|
|
||||||
|
local params = urlEncode('name=John Doe&age=30')
|
||||||
|
trace.log(params) -- name%3DJohn%20Doe%26age%3D30
|
||||||
|
|
||||||
|
-- Building query strings
|
||||||
|
local searchTerm = 'cats & dogs'
|
||||||
|
local url = 'gurt://search.com/api?q=' .. urlEncode(searchTerm)
|
||||||
|
trace.log(url) -- gurt://search.com/api?q=cats%20%26%20dogs
|
||||||
|
```
|
||||||
|
|
||||||
|
### urlDecode(string)
|
||||||
|
|
||||||
|
Decodes a percent-encoded URL string back to its original form.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local decoded = urlDecode('hello%20world%21')
|
||||||
|
trace.log(decoded) -- hello world!
|
||||||
|
|
||||||
|
local params = urlDecode('name%3DJohn%20Doe%26age%3D30')
|
||||||
|
trace.log(params) -- name=John Doe&age=30
|
||||||
|
|
||||||
|
local queryParam = 'cats%20%26%20dogs'
|
||||||
|
local searchTerm = urlDecode(queryParam)
|
||||||
|
trace.log(searchTerm) -- cats & dogs
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON API
|
||||||
|
|
||||||
|
### JSON.stringify(data)
|
||||||
|
|
||||||
|
Converts Lua data to JSON string.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local data = {
|
||||||
|
name = 'Alice',
|
||||||
|
age = 30,
|
||||||
|
hobbies = {'reading', 'coding'},
|
||||||
|
active = true
|
||||||
|
}
|
||||||
|
|
||||||
|
local jsonString = JSON.stringify(data)
|
||||||
|
trace.log(jsonString) -- {"name":"Alice","age":30,"hobbies":["reading","coding"],"active":true}
|
||||||
|
```
|
||||||
|
|
||||||
|
### JSON.parse(jsonString)
|
||||||
|
|
||||||
|
Parses JSON string to Lua data.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local jsonString = '{"name":"Bob","score":95.5}'
|
||||||
|
local data, error = JSON.parse(jsonString)
|
||||||
|
|
||||||
|
if data then
|
||||||
|
trace.log('Name: ' .. data.name)
|
||||||
|
trace.log('Score: ' .. data.score)
|
||||||
|
else
|
||||||
|
trace.log('Parse error: ' .. error)
|
||||||
|
end
|
||||||
|
```
|
||||||
46
docs/docs/lua/regex.md
Normal file
46
docs/docs/lua/regex.md
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# Regex API
|
||||||
|
|
||||||
|
Pattern matching and text processing with regular expressions.
|
||||||
|
|
||||||
|
## Regex.new(pattern)
|
||||||
|
|
||||||
|
Creates a new regex object from a pattern string.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local emailPattern = Regex.new('[a-zA-Z]+@[a-zA-Z]+\\.[a-zA-Z]+')
|
||||||
|
local phonePattern = Regex.new('\\(\\d{3}\\)\\s*\\d{3}-\\d{4}')
|
||||||
|
```
|
||||||
|
|
||||||
|
## regex:test(text)
|
||||||
|
|
||||||
|
Tests if the pattern matches anywhere in the text. Returns `true` or `false`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pattern = Regex.new('[a-zA-Z]+@[a-zA-Z]+\\.[a-zA-Z]+')
|
||||||
|
|
||||||
|
if pattern:test('user@example.com') then
|
||||||
|
trace.log('Valid email format')
|
||||||
|
end
|
||||||
|
|
||||||
|
if pattern:test('Contact us at admin@site.com') then
|
||||||
|
trace.log('Found email in text')
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## regex:match(text)
|
||||||
|
|
||||||
|
Finds the first match and returns capture groups as an array, or `nil` if no match found.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local pattern = Regex.new('(\\w+)@(\\w+)\\.(\\w+)')
|
||||||
|
local result = pattern:match('Contact: admin@site.com for help')
|
||||||
|
|
||||||
|
if result then
|
||||||
|
trace.log('Full match: ' .. result[1]) -- admin@site.com
|
||||||
|
trace.log('Username: ' .. result[2]) -- admin
|
||||||
|
trace.log('Domain: ' .. result[3]) -- site
|
||||||
|
trace.log('TLD: ' .. result[4]) -- com
|
||||||
|
else
|
||||||
|
trace.log('No match found')
|
||||||
|
end
|
||||||
|
```
|
||||||
61
docs/docs/lua/utils.md
Normal file
61
docs/docs/lua/utils.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Additional utilities
|
||||||
|
|
||||||
|
Gurted includes several helpful utilities:
|
||||||
|
|
||||||
|
### print(...)
|
||||||
|
We modify the global `print()` function to log to the browser console, and also convert any type (e.g. tables) to a readable string.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
print('Hello, world!')
|
||||||
|
print({ name = 'Alice', age = 30, hobbies = {'reading', 'coding'} }) -- {age=30,hobbies={1="reading",2="coding"},name="Alice"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### table.tostring(table)
|
||||||
|
|
||||||
|
Converts a table to a readable string representation.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local data = { name = 'John', age = 30, hobbies = {'reading', 'coding'} }
|
||||||
|
local str = table.tostring(data) -- {age=30,hobbies={1="reading",2="coding"},name="John"}
|
||||||
|
```
|
||||||
|
|
||||||
|
### string.replace(text, search, replacement)
|
||||||
|
|
||||||
|
Replaces the first occurrence of a string or regex pattern.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local text = 'Hello world, hello universe'
|
||||||
|
local result = string.replace(text, 'hello', 'hi')
|
||||||
|
trace.log(result) -- Hello world, hi universe
|
||||||
|
|
||||||
|
local pattern = Regex.new('\\b\\w+@\\w+\\.\\w+\\b')
|
||||||
|
local masked = string.replace('Email: john@test.com', pattern, '[EMAIL]')
|
||||||
|
trace.log(masked) -- Email: [EMAIL]
|
||||||
|
```
|
||||||
|
|
||||||
|
### string.replaceAll(text, search, replacement)
|
||||||
|
|
||||||
|
Replaces all occurrences of a string or regex pattern.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local text = 'Hello world, hello universe'
|
||||||
|
local result = string.replaceAll(text, 'hello', 'hi')
|
||||||
|
trace.log(result) -- Hello world, hi universe
|
||||||
|
|
||||||
|
local pattern = Regex.new('\\b\\w+@\\w+\\.\\w+\\b')
|
||||||
|
local text = 'Emails: john@test.com, jane@demo.org'
|
||||||
|
local masked = string.replaceAll(text, pattern, '[EMAIL]')
|
||||||
|
trace.log(masked) -- Emails: [EMAIL], [EMAIL]
|
||||||
|
```
|
||||||
|
|
||||||
|
### string.trim(text)
|
||||||
|
|
||||||
|
Removes whitespace from the beginning and end of a string.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local messy = ' Hello World '
|
||||||
|
local clean = string.trim(messy)
|
||||||
|
trace.log('"' .. clean .. '"') -- "Hello World"
|
||||||
|
```
|
||||||
|
|
||||||
|
This is particularly useful for debugging and logging complex data structures.
|
||||||
@@ -89,9 +89,8 @@ const config: Config = {
|
|||||||
position: 'left',
|
position: 'left',
|
||||||
label: 'Tutorial',
|
label: 'Tutorial',
|
||||||
},
|
},
|
||||||
{to: '/blog', label: 'Blog', position: 'left'},
|
|
||||||
{
|
{
|
||||||
href: 'https://github.com/facebook/docusaurus',
|
href: 'https://github.com/outpoot/gurted',
|
||||||
label: 'GitHub',
|
label: 'GitHub',
|
||||||
position: 'right',
|
position: 'right',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -39,7 +39,21 @@ const sidebars: SidebarsConfig = {
|
|||||||
items: [
|
items: [
|
||||||
'html',
|
'html',
|
||||||
'css',
|
'css',
|
||||||
'lua',
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Lua',
|
||||||
|
items: [
|
||||||
|
'lua/intro',
|
||||||
|
'lua/elements',
|
||||||
|
'lua/audio',
|
||||||
|
'lua/canvas',
|
||||||
|
'lua/network',
|
||||||
|
'lua/regex',
|
||||||
|
'lua/handling',
|
||||||
|
'lua/utils'
|
||||||
|
|
||||||
|
],
|
||||||
|
},
|
||||||
'postprocess',
|
'postprocess',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
4
flumi/README.md
Normal file
4
flumi/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Flumi
|
||||||
|
|
||||||
|
The official wayfinder, written in godot.
|
||||||
|
For more informations check out the [Gurted Documentation](https://docs.gurted.com).
|
||||||
Binary file not shown.
41
flumi/export_presets.cfg
Normal file
41
flumi/export_presets.cfg
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
[preset.0]
|
||||||
|
|
||||||
|
name="Linux"
|
||||||
|
platform="Linux"
|
||||||
|
runnable=true
|
||||||
|
advanced_options=false
|
||||||
|
dedicated_server=false
|
||||||
|
custom_features=""
|
||||||
|
export_filter="all_resources"
|
||||||
|
include_filter=""
|
||||||
|
exclude_filter=""
|
||||||
|
export_path="../../../Flumi.x86_64"
|
||||||
|
patches=PackedStringArray()
|
||||||
|
encryption_include_filters=""
|
||||||
|
encryption_exclude_filters=""
|
||||||
|
seed=0
|
||||||
|
encrypt_pck=false
|
||||||
|
encrypt_directory=false
|
||||||
|
script_export_mode=2
|
||||||
|
|
||||||
|
[preset.0.options]
|
||||||
|
|
||||||
|
custom_template/debug=""
|
||||||
|
custom_template/release=""
|
||||||
|
debug/export_console_wrapper=1
|
||||||
|
binary_format/embed_pck=false
|
||||||
|
texture_format/s3tc_bptc=true
|
||||||
|
texture_format/etc2_astc=false
|
||||||
|
binary_format/architecture="x86_64"
|
||||||
|
ssh_remote_deploy/enabled=false
|
||||||
|
ssh_remote_deploy/host="user@host_ip"
|
||||||
|
ssh_remote_deploy/port="22"
|
||||||
|
ssh_remote_deploy/extra_args_ssh=""
|
||||||
|
ssh_remote_deploy/extra_args_scp=""
|
||||||
|
ssh_remote_deploy/run_script="#!/usr/bin/env bash
|
||||||
|
export DISPLAY=:0
|
||||||
|
unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\"
|
||||||
|
\"{temp_dir}/{exe_name}\" {cmd_args}"
|
||||||
|
ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash
|
||||||
|
kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\")
|
||||||
|
rm -rf \"{temp_dir}\""
|
||||||
BIN
images/flumi.png
Normal file
BIN
images/flumi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 57 KiB |
@@ -1,7 +1,7 @@
|
|||||||
The implementation of the GURT protocol.
|
The implementation of the GURT protocol.
|
||||||
|
|
||||||
- [cli](./cli) - Gurty, the command-line tool for managing GURT servers
|
- [Cli](./cli) - Gurty, the command-line tool for managing GURT servers
|
||||||
- [library](./library) - client/server GURT protocol library for Rust (the core, used in Flumi)
|
- [Library](./library) - client/server GURT protocol library for Rust (the core, used in Flumi)
|
||||||
- [gdextension](./gdextension) - the Godot bindings for the GURT protocol (used in Flumi)
|
- [Gdextension](./gdextension) - the Godot bindings for the GURT protocol (used in Flumi)
|
||||||
|
|
||||||
For the full spec, see the [Gurted Documentation](https://docs.gurted.com).
|
For the full spec, check the [Gurted Documentation](https://docs.gurted.com).
|
||||||
@@ -1,5 +1,75 @@
|
|||||||
The official Gurted search engine, Ringle.
|
# Ringle
|
||||||
|
|
||||||
Copy `config.template.toml` to `config.toml` and edit as needed.
|
The official Gurted search engine.
|
||||||
|
|
||||||
Run with `cargo run`
|
## Configuration
|
||||||
|
```sh
|
||||||
|
cp config.template.toml config.toml
|
||||||
|
```
|
||||||
|
### Values
|
||||||
|
```toml
|
||||||
|
[database]
|
||||||
|
url = "postgres://..." # A valid postgres database url
|
||||||
|
max_connections = 5 # The maximum amount of simultaneous connections to the database
|
||||||
|
```
|
||||||
|
```toml
|
||||||
|
[server]
|
||||||
|
address = "127.0.0.1" # The binding adress the server will listen to
|
||||||
|
port = 4879 # The port the server will listen on
|
||||||
|
cert_path = "certs/t.crt" # A path to the certificate
|
||||||
|
key_path = "certs/t.key" # A path to the key for the certificate
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[search]
|
||||||
|
index_path = "./search_indexes" # The path where the indexed pages will be saved to
|
||||||
|
crawl_interval_hours = 2 # How frequently should the search engine crawl (in hours)
|
||||||
|
max_pages_per_domain = 1000 # Maximum amount of pages indexed per domain
|
||||||
|
crawler_timeout_seconds = 30 # The maximum amount of seconds before a page times out and is skipped
|
||||||
|
crawler_user_agent = "RingleBot/1.0" # The user agent the crawler should use
|
||||||
|
max_concurrent_crawls = 5 # How many pages should the bot crawl concurrently
|
||||||
|
content_size_limit_mb = 10 # The maximum amount of data a page can be
|
||||||
|
index_rebuild_interval_hours = 48 # How often (in hours) should the index be rebuilt
|
||||||
|
search_results_per_page = 20 # How many search results should be displayed per page
|
||||||
|
max_search_results = 1000 # The maximum amount of results displayed
|
||||||
|
|
||||||
|
allowed_extensions = [ # Extensions allowed to be indexed
|
||||||
|
"html", "htm", "txt", "md", "json", "xml", "rss", "atom"
|
||||||
|
]
|
||||||
|
|
||||||
|
blocked_extensions = [ # Extension that should not be indexed
|
||||||
|
"exe", "zip", "rar", "tar", "gz", "7z", "iso", "dmg",
|
||||||
|
"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx",
|
||||||
|
"jpg", "jpeg", "png", "gif", "bmp", "svg", "webp",
|
||||||
|
"mp3", "mp4", "avi", "mov", "wmv", "flv", "webm",
|
||||||
|
"css", "js", "woff", "woff2", "ttf", "eot"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[crawler]
|
||||||
|
clanker_txt = true # Wheter or not should the crawler respect clanker.txt
|
||||||
|
crawl_delay_ms = 1000 # The delay between each page crawl
|
||||||
|
max_redirects = 5 # The maximum amount of redirects the crawler shoul follow
|
||||||
|
follow_external_links = false # Crawl external links found in the page?
|
||||||
|
max_depth = 10 # The maximum amount of nested pages
|
||||||
|
|
||||||
|
request_headers = [ # The headers the crawler will include in the request while crawling
|
||||||
|
["Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"],
|
||||||
|
["Accept-Language", "en-US,en;q=0.5"],
|
||||||
|
["Accept-Encoding", "gzip, deflate"],
|
||||||
|
["DNT", "1"],
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[logging]
|
||||||
|
level = "info" # How much should the search engine log, can be info, debug or trace
|
||||||
|
format = "compact" # The format for the logs
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running
|
||||||
|
Run with:
|
||||||
|
```sh
|
||||||
|
cargo run
|
||||||
|
```
|
||||||
@@ -13,7 +13,7 @@ index_path = "./search_indexes"
|
|||||||
crawl_interval_hours = 2
|
crawl_interval_hours = 2
|
||||||
max_pages_per_domain = 1000
|
max_pages_per_domain = 1000
|
||||||
crawler_timeout_seconds = 30
|
crawler_timeout_seconds = 30
|
||||||
crawler_user_agent = "GurtedSearchBot/1.0"
|
crawler_user_agent = "RingleBot/1.0"
|
||||||
max_concurrent_crawls = 5
|
max_concurrent_crawls = 5
|
||||||
content_size_limit_mb = 10
|
content_size_limit_mb = 10
|
||||||
index_rebuild_interval_hours = 48
|
index_rebuild_interval_hours = 48
|
||||||
|
|||||||
@@ -1,22 +1,8 @@
|
|||||||
# sv
|
# Gurted documentation
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
|
||||||
|
|
||||||
## Creating a project
|
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# create a new project in the current directory
|
|
||||||
npx sv create
|
|
||||||
|
|
||||||
# create a new project in my-app
|
|
||||||
npx sv create my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
Once you've installed the required dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run dev
|
npm run dev
|
||||||
@@ -27,12 +13,10 @@ npm run dev -- --open
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
To create a production version of your app:
|
To create a production version of the documentation page:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
You can preview the production build with `npm run preview`.
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user