diff --git a/SPEC.md b/SPEC.md
index 64ec47b..a1271d2 100644
--- a/SPEC.md
+++ b/SPEC.md
@@ -118,11 +118,12 @@ Commas are **required** between items, with **trailing commas allowed**:
null // Null value
boolean // True or false
integer // 64-bit integer
-number // Double-precision float
string // UTF-8 string
bytes // Byte array
```
+**Note:** ATProto Lexicons do not support floating-point numbers. Only `integer` is available for numeric values.
+
### Special String Formats
Defined in `prelude.mlf` and available everywhere:
@@ -914,7 +915,7 @@ Lexicons are versioned at the NSID level. MLF files should include version metad
```
as, blob, boolean, bytes, constrained, def, error, inline, integer,
-null, number, procedure, query, record, string, subscription, token,
+null, procedure, query, record, string, subscription, token,
type, unknown, use
```
diff --git a/mlf-codegen/src/lib.rs b/mlf-codegen/src/lib.rs
index ab2dd9d..a43efdb 100644
--- a/mlf-codegen/src/lib.rs
+++ b/mlf-codegen/src/lib.rs
@@ -461,7 +461,6 @@ fn generate_primitive_json(kind: PrimitiveType) -> Value {
PrimitiveType::Null => json!({ "type": "null" }),
PrimitiveType::Boolean => json!({ "type": "boolean" }),
PrimitiveType::Integer => json!({ "type": "integer" }),
- PrimitiveType::Number => json!({ "type": "number" }),
PrimitiveType::String => json!({ "type": "string" }),
PrimitiveType::Bytes => json!({ "type": "bytes" }),
PrimitiveType::Blob => json!({ "type": "blob" }),
diff --git a/mlf-lang/src/ast.rs b/mlf-lang/src/ast.rs
index 34af890..4825560 100644
--- a/mlf-lang/src/ast.rs
+++ b/mlf-lang/src/ast.rs
@@ -285,7 +285,6 @@ pub enum PrimitiveType {
Null,
Boolean,
Integer,
- Number,
String,
Bytes,
Blob,
diff --git a/mlf-lang/src/lexer.rs b/mlf-lang/src/lexer.rs
index cbdff52..d66ba4e 100644
--- a/mlf-lang/src/lexer.rs
+++ b/mlf-lang/src/lexer.rs
@@ -26,7 +26,6 @@ pub enum Token {
Integer,
Namespace,
Null,
- Number,
Procedure,
Query,
Record,
@@ -81,7 +80,6 @@ impl core::fmt::Display for Token {
Token::Integer => write!(f, "integer"),
Token::Namespace => write!(f, "namespace"),
Token::Null => write!(f, "null"),
- Token::Number => write!(f, "number"),
Token::Procedure => write!(f, "procedure"),
Token::Query => write!(f, "query"),
Token::Record => write!(f, "record"),
@@ -155,7 +153,6 @@ fn identifier(input: &str) -> IResult<&str, Token> {
"integer" => Token::Integer,
"namespace" => Token::Namespace,
"null" => Token::Null,
- "number" => Token::Number,
"procedure" => Token::Procedure,
"query" => Token::Query,
"record" => Token::Record,
diff --git a/mlf-lang/src/parser.rs b/mlf-lang/src/parser.rs
index 41b6d2d..4d8f0d1 100644
--- a/mlf-lang/src/parser.rs
+++ b/mlf-lang/src/parser.rs
@@ -79,7 +79,6 @@ impl Parser {
LexToken::As => "as".into(),
LexToken::String => "string".into(),
LexToken::Integer => "integer".into(),
- LexToken::Number => "number".into(),
LexToken::Boolean => "boolean".into(),
LexToken::Null => "null".into(),
LexToken::Unknown => "unknown".into(),
@@ -658,13 +657,6 @@ impl Parser {
span,
}
}
- LexToken::Number => {
- let span = self.advance().span;
- Type::Primitive {
- kind: PrimitiveType::Number,
- span,
- }
- }
LexToken::Boolean => {
let span = self.advance().span;
Type::Primitive {
diff --git a/mlf-lang/src/workspace.rs b/mlf-lang/src/workspace.rs
index 8052080..d235fae 100644
--- a/mlf-lang/src/workspace.rs
+++ b/mlf-lang/src/workspace.rs
@@ -261,7 +261,7 @@ impl Workspace {
}
Constraint::Minimum { span, .. }
| Constraint::Maximum { span, .. } => {
- if !matches!(base_kind, Some(PrimitiveType::Integer) | Some(PrimitiveType::Number)) {
+ if !matches!(base_kind, Some(PrimitiveType::Integer)) {
errors.push(ValidationError::InvalidConstraint {
message: alloc::format!("Numeric constraint on non-numeric type"),
span: *span,
diff --git a/mlf-validation/src/lib.rs b/mlf-validation/src/lib.rs
index 2f502e1..0585a2b 100644
--- a/mlf-validation/src/lib.rs
+++ b/mlf-validation/src/lib.rs
@@ -173,14 +173,6 @@ impl<'a> RecordValidator<'a> {
});
}
}
- PrimitiveType::Number => {
- if !value.is_f64() && !value.is_i64() {
- errors.push(ValidationError {
- path: path.to_string(),
- message: "Expected number".to_string(),
- });
- }
- }
PrimitiveType::String => {
if !value.is_string() {
errors.push(ValidationError {
diff --git a/tree-sitter-mlf/grammar.js b/tree-sitter-mlf/grammar.js
index faa1aad..fdcde39 100644
--- a/tree-sitter-mlf/grammar.js
+++ b/tree-sitter-mlf/grammar.js
@@ -16,6 +16,10 @@ module.exports = grammar({
$.comment,
],
+ conflicts: $ => [
+ [$.type, $.union_type]
+ ],
+
rules: {
source_file: $ => repeat($.item),
@@ -95,6 +99,7 @@ module.exports = grammar({
'query',
field('name', $.identifier),
field('params', $.parameter_list),
+ ':',
field('return', $.return_type),
';'
),
@@ -104,6 +109,7 @@ module.exports = grammar({
'procedure',
field('name', $.identifier),
field('params', $.parameter_list),
+ ':',
field('return', $.return_type),
';'
),
@@ -113,7 +119,7 @@ module.exports = grammar({
'subscription',
field('name', $.identifier),
field('params', $.parameter_list),
- '->',
+ ':',
field('messages', $.type),
';'
),
@@ -136,15 +142,15 @@ module.exports = grammar({
),
return_type: $ => choice(
- seq('->', $.type),
- seq(
- '->',
+ prec(1, seq(
$.type,
- 'throws',
+ '|',
+ 'error',
'{',
repeat($.error_definition),
'}'
- )
+ )),
+ $.type
),
error_definition: $ => seq(
@@ -171,7 +177,6 @@ module.exports = grammar({
'null',
'boolean',
'integer',
- 'number',
'string',
'bytes',
'blob',
diff --git a/tree-sitter-mlf/queries/highlights.scm b/tree-sitter-mlf/queries/highlights.scm
index 2a8e715..fe55fa0 100644
--- a/tree-sitter-mlf/queries/highlights.scm
+++ b/tree-sitter-mlf/queries/highlights.scm
@@ -1,14 +1,15 @@
; Keywords
[
- "namespace"
"use"
"record"
- "alias"
+ "def"
+ "inline"
+ "type"
"token"
"query"
"procedure"
"subscription"
- "throws"
+ "error"
"constrained"
] @keyword
@@ -17,7 +18,6 @@
"null"
"boolean"
"integer"
- "number"
"string"
"bytes"
"blob"
@@ -42,8 +42,11 @@
(record_definition
name: (identifier) @type)
-; Alias names
-(alias_definition
+; Type definition names
+(def_type_definition
+ name: (identifier) @type)
+
+(inline_type_definition
name: (identifier) @type)
; Token names
@@ -75,7 +78,6 @@
[
":"
"="
- "->"
"|"
"?"
] @operator
diff --git a/tree-sitter-mlf/src/grammar.json b/tree-sitter-mlf/src/grammar.json
index 1ea4a4f..d035b9b 100644
--- a/tree-sitter-mlf/src/grammar.json
+++ b/tree-sitter-mlf/src/grammar.json
@@ -11,10 +11,6 @@
"item": {
"type": "CHOICE",
"members": [
- {
- "type": "SYMBOL",
- "name": "namespace_declaration"
- },
{
"type": "SYMBOL",
"name": "use_statement"
@@ -81,31 +77,6 @@
]
}
},
- "namespace_declaration": {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "namespace"
- },
- {
- "type": "FIELD",
- "name": "name",
- "content": {
- "type": "SYMBOL",
- "name": "namespace_identifier"
- }
- },
- {
- "type": "STRING",
- "value": ";"
- }
- ]
- },
- "namespace_identifier": {
- "type": "PATTERN",
- "value": "[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)*"
- },
"use_statement": {
"type": "SEQ",
"members": [
@@ -149,10 +120,6 @@
"type": "SYMBOL",
"name": "record_body"
}
- },
- {
- "type": "STRING",
- "value": ";"
}
]
},
@@ -347,6 +314,10 @@
"name": "parameter_list"
}
},
+ {
+ "type": "STRING",
+ "value": ":"
+ },
{
"type": "FIELD",
"name": "return",
@@ -384,6 +355,10 @@
"name": "parameter_list"
}
},
+ {
+ "type": "STRING",
+ "value": ":"
+ },
{
"type": "FIELD",
"name": "return",
@@ -423,7 +398,7 @@
},
{
"type": "STRING",
- "value": "->"
+ "value": ":"
},
{
"type": "FIELD",
@@ -538,49 +513,44 @@
"type": "CHOICE",
"members": [
{
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "->"
- },
- {
- "type": "SYMBOL",
- "name": "type"
- }
- ]
- },
- {
- "type": "SEQ",
- "members": [
- {
- "type": "STRING",
- "value": "->"
- },
- {
- "type": "SYMBOL",
- "name": "type"
- },
- {
- "type": "STRING",
- "value": "throws"
- },
- {
- "type": "STRING",
- "value": "{"
- },
- {
- "type": "REPEAT",
- "content": {
+ "type": "PREC",
+ "value": 1,
+ "content": {
+ "type": "SEQ",
+ "members": [
+ {
"type": "SYMBOL",
- "name": "error_definition"
+ "name": "type"
+ },
+ {
+ "type": "STRING",
+ "value": "|"
+ },
+ {
+ "type": "STRING",
+ "value": "error"
+ },
+ {
+ "type": "STRING",
+ "value": "{"
+ },
+ {
+ "type": "REPEAT",
+ "content": {
+ "type": "SYMBOL",
+ "name": "error_definition"
+ }
+ },
+ {
+ "type": "STRING",
+ "value": "}"
}
- },
- {
- "type": "STRING",
- "value": "}"
- }
- ]
+ ]
+ }
+ },
+ {
+ "type": "SYMBOL",
+ "name": "type"
}
]
},
@@ -666,10 +636,6 @@
"type": "STRING",
"value": "integer"
},
- {
- "type": "STRING",
- "value": "number"
- },
{
"type": "STRING",
"value": "string"
@@ -1030,7 +996,12 @@
"name": "comment"
}
],
- "conflicts": [],
+ "conflicts": [
+ [
+ "type",
+ "union_type"
+ ]
+ ],
"precedences": [],
"externals": [],
"inline": [],
diff --git a/tree-sitter-mlf/src/node-types.json b/tree-sitter-mlf/src/node-types.json
index d07acd2..81685fa 100644
--- a/tree-sitter-mlf/src/node-types.json
+++ b/tree-sitter-mlf/src/node-types.json
@@ -264,10 +264,6 @@
"type": "inline_type_definition",
"named": true
},
- {
- "type": "namespace_declaration",
- "named": true
- },
{
"type": "procedure_definition",
"named": true
@@ -295,22 +291,6 @@
]
}
},
- {
- "type": "namespace_declaration",
- "named": true,
- "fields": {
- "name": {
- "multiple": false,
- "required": true,
- "types": [
- {
- "type": "namespace_identifier",
- "named": true
- }
- ]
- }
- }
- },
{
"type": "non_union_type",
"named": true,
@@ -694,10 +674,6 @@
"type": ",",
"named": false
},
- {
- "type": "->",
- "named": false
- },
{
"type": ".",
"named": false
@@ -759,25 +735,21 @@
"named": true
},
{
- "type": "false",
+ "type": "error",
"named": false
},
{
- "type": "inline",
+ "type": "false",
"named": false
},
{
- "type": "integer",
+ "type": "inline",
"named": false
},
{
- "type": "namespace",
+ "type": "integer",
"named": false
},
- {
- "type": "namespace_identifier",
- "named": true
- },
{
"type": "null",
"named": false
@@ -786,10 +758,6 @@
"type": "number",
"named": true
},
- {
- "type": "number",
- "named": false
- },
{
"type": "procedure",
"named": false
@@ -814,10 +782,6 @@
"type": "subscription",
"named": false
},
- {
- "type": "throws",
- "named": false
- },
{
"type": "token",
"named": false
diff --git a/website/content/docs/language-guide/02-fields.md b/website/content/docs/language-guide/02-fields.md
index ea7f1f0..b11548d 100644
--- a/website/content/docs/language-guide/02-fields.md
+++ b/website/content/docs/language-guide/02-fields.md
@@ -37,13 +37,7 @@ record example {
}
```
-**Numbers** (double-precision floats):
-```mlf
-record example {
- price: number,
- rating: number,
-}
-```
+**Note:** ATProto Lexicons only support integers for numeric values. There is no float/double type.
**Booleans:**
```mlf
@@ -114,8 +108,8 @@ record profile {
location: {
city: string,
coordinates: {
- lat: number,
- lng: number,
+ lat: integer,
+ lng: integer,
},
},
}
@@ -178,8 +172,8 @@ record post {
/// Optional geographic location
location?: {
name: string,
- lat: number,
- lng: number,
+ lat: integer,
+ lng: integer,
},
/// Tags on this post
diff --git a/website/content/docs/language-guide/03-constraints.md b/website/content/docs/language-guide/03-constraints.md
index e8e13ad..34f76cb 100644
--- a/website/content/docs/language-guide/03-constraints.md
+++ b/website/content/docs/language-guide/03-constraints.md
@@ -93,21 +93,7 @@ count: integer constrained {
}
```
-## Number Constraints
-
-Numbers (floats) work the same as integers:
-
-```mlf
-rating: number constrained {
- minimum: 0.0,
- maximum: 5.0,
-}
-
-price: number constrained {
- minimum: 0.0,
- default: 0.0,
-}
-```
+**Note:** ATProto Lexicons do not support a `number`/`float` type. Only `integer` is supported for numeric values.
## Boolean Constraints
@@ -238,10 +224,10 @@ record thread {
maxSize: 500000,
},
- /// Average rating (0-5 stars)
- rating?: number constrained {
- minimum: 0.0,
- maximum: 5.0,
+ /// Average rating (0-5 scale)
+ rating?: integer constrained {
+ minimum: 0,
+ maximum: 5,
},
/// Whether thread is featured
diff --git a/website/content/docs/language-guide/04-custom-types.md b/website/content/docs/language-guide/04-custom-types.md
index 6ded98d..36d81f9 100644
--- a/website/content/docs/language-guide/04-custom-types.md
+++ b/website/content/docs/language-guide/04-custom-types.md
@@ -62,8 +62,8 @@ Inline types can define objects too:
```mlf
inline type Coordinates = {
- lat: number,
- lng: number,
+ lat: integer,
+ lng: integer,
};
record location {
diff --git a/website/content/docs/language-guide/08-imports.md b/website/content/docs/language-guide/08-imports.md
index 5c7a421..92c56e5 100644
--- a/website/content/docs/language-guide/08-imports.md
+++ b/website/content/docs/language-guide/08-imports.md
@@ -223,4 +223,4 @@ Both `post.mlf` and `comment.mlf` import and reuse the same `author` and `postRe
## What's Next?
-Finally, let's learn about the prelude - built-in types available in every file.
+Next, let's learn about the prelude - built-in types available in every file. After that, we'll cover important information about how MLF maps to ATProto Lexicons.
diff --git a/website/content/docs/language-guide/09-prelude.md b/website/content/docs/language-guide/09-prelude.md
index 9d5daa4..9aef57a 100644
--- a/website/content/docs/language-guide/09-prelude.md
+++ b/website/content/docs/language-guide/09-prelude.md
@@ -193,4 +193,8 @@ The prelude provides:
You've now learned all the core features of MLF! You can define records, add constraints, create custom types, use unions and tokens, define XRPC operations, import from other files, and use prelude types.
-Check out the [Playground](/playground/) to experiment with MLF, or read the [CLI documentation](/docs/cli/) to learn how to compile your lexicons.
+## What's Next?
+
+Read the [Important Info](/docs/language-guide/10-important-info/) section to understand how MLF maps to ATProto Lexicons, especially the rules for the `"main"` definition.
+
+Then check out the [Playground](/playground/) to experiment with MLF, or read the [CLI documentation](/docs/cli/) to learn how to compile your lexicons.
diff --git a/website/content/docs/language-guide/10-important-info.md b/website/content/docs/language-guide/10-important-info.md
new file mode 100644
index 0000000..9c38d72
--- /dev/null
+++ b/website/content/docs/language-guide/10-important-info.md
@@ -0,0 +1,220 @@
++++
+title = "Important Info"
+weight = 10
++++
+
+This section covers important details about how MLF maps to ATProto Lexicons.
+
+## The "main" Definition
+
+In ATProto Lexicons, each lexicon has a `defs` object where definitions are stored. One special definition is called `"main"` - it's the primary definition for that lexicon.
+
+### When Does a Definition Become "main"?
+
+MLF automatically determines which definition becomes `"main"` based on two rules:
+
+**Rule 1: Single Main-Eligible Item**
+
+If your file contains **exactly one** record, query, procedure, or subscription, it automatically becomes `"main"`:
+
+```mlf
+// File: com/example/forum/post.mlf
+record post {
+ text: string,
+ author: Did,
+}
+```
+
+This generates:
+```json
+{
+ "lexicon": 1,
+ "id": "com.example.forum.post",
+ "defs": {
+ "main": {
+ "type": "record",
+ ...
+ }
+ }
+}
+```
+
+**Rule 2: Name Matches NSID Fragment**
+
+If your definition name matches the **last segment** of the NSID, it becomes `"main"`:
+
+```mlf
+// File: com/example/forum/post.mlf
+record post { // Name "post" matches last NSID segment
+ text: string,
+}
+
+def type author = {
+ did: Did,
+};
+```
+
+The `post` record becomes `"main"`, while `author` becomes a named def:
+
+```json
+{
+ "lexicon": 1,
+ "id": "com.example.forum.post",
+ "defs": {
+ "main": {
+ "type": "record",
+ ...
+ },
+ "author": {
+ "type": "object",
+ ...
+ }
+ }
+}
+```
+
+### Multiple Main-Eligible Items
+
+If you have multiple main-eligible items and none match the NSID fragment, they all become named defs:
+
+```mlf
+// File: com/example/forum/thread.mlf
+record post { // Doesn't match "thread"
+ text: string,
+}
+
+record reply { // Doesn't match "thread"
+ text: string,
+}
+```
+
+Generates:
+```json
+{
+ "lexicon": 1,
+ "id": "com.example.forum.thread",
+ "defs": {
+ "post": { ... },
+ "reply": { ... }
+ }
+}
+```
+
+**Note:** Neither becomes `"main"` because the file is named `thread.mlf` but contains `post` and `reply`.
+
+### Supporting Definitions
+
+These are **never** `"main"` - they're always named defs:
+- `def type` definitions
+- `token` definitions
+- `inline type` definitions (don't appear in output at all)
+
+### Best Practices
+
+**For single-definition files:**
+```mlf
+// File: com/example/forum/post.mlf
+record post {
+ // The record name matches the filename
+ text: string,
+}
+```
+
+**For multi-definition files:**
+```mlf
+// File: com/example/forum/post.mlf
+record post { // Becomes "main" (matches NSID)
+ text: string,
+ author: author,
+}
+
+def type author = { // Named def: "author"
+ did: Did,
+ handle: Handle,
+};
+
+token draft; // Named def: "draft"
+token published; // Named def: "published"
+```
+
+**For shared types files:**
+```mlf
+// File: com/example/forum/defs.mlf
+def type author = {
+ did: Did,
+};
+
+def type postRef = {
+ uri: AtUri,
+};
+```
+
+When the NSID ends with `defs`, all items become named defs (no `"main"`).
+
+## NSID and File Path Mapping
+
+The file path **is** the NSID. MLF derives the lexicon NSID from the file path:
+
+| File Path | NSID | Last Segment |
+|-----------|------|--------------|
+| `com/example/forum/post.mlf` | `com.example.forum.post` | `post` |
+| `com/example/forum/thread.mlf` | `com.example.forum.thread` | `thread` |
+| `com/example/forum/defs.mlf` | `com.example.forum.defs` | `defs` |
+
+The last segment determines which definition becomes `"main"` (when using Rule 2).
+
+## Reserved Names
+
+You **cannot** use these as definition names under any circumstances:
+- `main` - Reserved for the main definition
+- `defs` - Reserved for the definitions container
+
+MLF will reject these names even with raw identifiers (backticks).
+
+## Reserved Keywords and Raw Identifiers
+
+MLF has reserved keywords that normally cannot be used as field or type names:
+
+```
+as, blob, boolean, bytes, constrained, def, error, inline, integer,
+null, procedure, query, record, string, subscription, token,
+type, unknown, use
+```
+
+**Note:** The `number` type is not supported by ATProto Lexicons. Only `integer` is available for numeric values.
+
+However, you can use keywords as identifiers by wrapping them in **backticks** (`` ` ``):
+
+```mlf
+def type metadata = {
+ `type`: string, // "type" is a keyword, escaped with backticks
+ `record`: AtUri, // "record" is a keyword, escaped
+ `error`: string, // "error" is a keyword, escaped
+ name: string, // "name" is not a keyword, no escaping needed
+};
+```
+
+This is useful for compatibility with existing ATProto schemas that use these names.
+
+**Type names can also be escaped:**
+```mlf
+def type `record` = { // Type name "record" escaped
+ uri: AtUri,
+ cid: Cid,
+};
+```
+
+**When you need raw identifiers:**
+- Importing existing ATProto lexicons that use keyword names
+- Maintaining compatibility with JSON schemas
+- Working with established conventions in the ATProto ecosystem
+
+**Important:** This only works for field names and type names. You cannot escape `main` or `defs` - they are always forbidden.
+
+## Summary
+
+- **Single main-eligible item** → automatically becomes `"main"`
+- **Name matches last NSID segment** → becomes `"main"`
+- **Neither condition met** → all items become named defs
+- **Supporting definitions** (def type, token) → always named defs
+- **File path** → determines the NSID
diff --git a/website/sass/style.scss b/website/sass/style.scss
index 45ab039..15eda56 100644
--- a/website/sass/style.scss
+++ b/website/sass/style.scss
@@ -680,6 +680,22 @@ footer {
font-size: 0.875rem;
}
+/* Mobile menu toggle */
+.mobile-menu-toggle {
+ display: none;
+ background: none;
+ border: none;
+ color: var(--text);
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0.5rem;
+ line-height: 1;
+}
+
+.mobile-menu-toggle:hover {
+ color: var(--accent);
+}
+
/* Responsive */
@media (max-width: 768px) {
.hero h1 {
@@ -699,9 +715,41 @@ footer {
grid-template-columns: 1fr;
}
+ /* Mobile navigation */
+ .mobile-menu-toggle {
+ display: block;
+ }
+
.nav-links {
- gap: 1rem;
- font-size: 0.875rem;
+ display: none;
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: var(--bg-elevated);
+ flex-direction: column;
+ gap: 0;
+ padding: 1rem 0;
+ border-bottom: 1px solid var(--border);
+ z-index: 50;
+ }
+
+ .nav-links.active {
+ display: flex;
+ }
+
+ .nav-links li {
+ width: 100%;
+ }
+
+ .nav-links a {
+ display: block;
+ padding: 0.75rem 1.5rem;
+ font-size: 1rem;
+ }
+
+ nav {
+ position: relative;
}
.cta-buttons {
@@ -717,24 +765,53 @@ footer {
.doc-layout {
grid-template-columns: 1fr;
- gap: 2rem;
+ gap: 1.5rem;
+ }
+
+ .doc-page {
+ padding: 1.5rem 0;
}
.doc-sidebar {
position: static;
- border-bottom: 1px solid var(--border);
- padding-bottom: 1.5rem;
+ border: 1px solid var(--border);
+ border-radius: 0.375rem;
+ padding: 1rem;
margin-bottom: 1.5rem;
}
- .doc-nav ul {
- display: flex;
- flex-wrap: wrap;
- gap: 0.5rem;
+ .doc-nav h3 {
+ cursor: pointer;
+ user-select: none;
}
- .doc-nav li {
- margin-bottom: 0;
+ .doc-nav h3::after {
+ content: " ▼";
+ font-size: 0.75rem;
+ color: var(--text-muted);
+ }
+
+ .doc-nav > ul {
+ max-height: 500px;
+ overflow-y: auto;
+ }
+
+ .doc-nav .nav-section ul {
+ padding-left: 0.5rem;
+ }
+
+ /* Make tables responsive */
+ .doc-content table {
+ display: block;
+ overflow-x: auto;
+ white-space: nowrap;
+ }
+
+ /* Code blocks */
+ .doc-content pre {
+ margin-left: -1.5rem;
+ margin-right: -1.5rem;
+ border-radius: 0;
}
}
@@ -751,13 +828,51 @@ footer {
font-size: 1.75rem;
}
- .nav-links {
- gap: 0.75rem;
+ .nav-container {
+ padding: 0 1rem;
+ }
+
+ .logo img {
+ height: 36px;
}
textarea {
height: 300px;
}
+
+ .doc-content {
+ font-size: 0.938rem;
+ }
+
+ .doc-content h1 {
+ font-size: 1.875rem;
+ }
+
+ .doc-content h2 {
+ font-size: 1.5rem;
+ }
+
+ .doc-content h3 {
+ font-size: 1.25rem;
+ }
+
+ .doc-content pre {
+ font-size: 0.813rem;
+ }
+
+ .panel-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .file-path-container {
+ width: 100%;
+ }
+
+ .tabs {
+ width: 100%;
+ justify-content: space-between;
+ }
}
/* Documentation pages */
@@ -927,6 +1042,34 @@ footer {
color: var(--text-light);
}
+.doc-content table {
+ width: 100%;
+ margin: 1.5rem 0;
+ border-collapse: collapse;
+ font-size: 0.875rem;
+}
+
+.doc-content table th,
+.doc-content table td {
+ padding: 0.75rem;
+ text-align: left;
+ border: 1px solid var(--border);
+}
+
+.doc-content table th {
+ background: var(--bg-alt);
+ font-weight: 600;
+ color: var(--text);
+}
+
+.doc-content table td {
+ color: var(--text-light);
+}
+
+.doc-content table code {
+ font-size: 0.813rem;
+}
+
.logo a {
color: var(--accent);
text-decoration: none;
diff --git a/website/static/js/app.js b/website/static/js/app.js
index 26ab42c..2c4359b 100644
--- a/website/static/js/app.js
+++ b/website/static/js/app.js
@@ -47,7 +47,7 @@ const mlfGrammar = {
patterns: [
{
name: 'storage.type.builtin.mlf',
- match: '\\b(null|boolean|integer|number|string|bytes|blob|unknown)\\b'
+ match: '\\b(null|boolean|integer|string|bytes|blob|unknown)\\b'
},
{
name: 'storage.type.format.mlf',
diff --git a/website/syntaxes/mlf.sublime-syntax b/website/syntaxes/mlf.sublime-syntax
index 37c755b..bb727a3 100644
--- a/website/syntaxes/mlf.sublime-syntax
+++ b/website/syntaxes/mlf.sublime-syntax
@@ -36,7 +36,7 @@ contexts:
types:
# Primitive types
- - match: '\b(null|boolean|integer|number|string|bytes|blob|unknown)\b'
+ - match: '\b(null|boolean|integer|string|bytes|blob|unknown)\b'
scope: storage.type.builtin.mlf
# Format types
@@ -73,5 +73,3 @@ contexts:
scope: keyword.operator.union.mlf
- match: '='
scope: keyword.operator.assignment.mlf
- - match: '->'
- scope: keyword.operator.arrow.mlf
diff --git a/website/templates/base.html b/website/templates/base.html
index 1330bc0..b174aaa 100644
--- a/website/templates/base.html
+++ b/website/templates/base.html
@@ -21,7 +21,10 @@
-