diff --git a/Micro-Markdown-Guide.md b/Micro-Markdown-Guide.md new file mode 100644 index 0000000..d9edc92 --- /dev/null +++ b/Micro-Markdown-Guide.md @@ -0,0 +1,103 @@ +# Micro Markdown + +Every bit of text on the map — station titles, station subtitles, and +free-standing text boxes — accepts a small set of formatting markers typed +directly into the text itself. No separate bold/italic controls to hunt for: +type the marker, the map renders it styled. + +It's called "micro" markdown on purpose. It's not the full Markdown you'd +write a README in — no headings, links, or lists — just enough inline +emphasis to make a label pop. + +## Syntax + +| You type | You get | +|---|---| +| `**bold**` | **bold** | +| `*italic*` | *italic* | +| `__underline__` | underlined text | +| `~~strikethrough~~` | struck-through text | +| `[#ff0000]colored[/]` | text in that hex color | +| `[w300]weighted[/]` | text at an explicit font weight | +| `24^th^` | superscript | +| `H~2~O` | subscript | + +A few examples: + +``` +**Oxford Circus** +Meet at *platform 3* +__Zone 1__ station +~~Closed~~ reopening soon +[#e32017]Central line[/] interchange +[w300]light[/] and [w900]heavy[/] in the same label +King's Cross 5^th^ entrance +``` + +## Nesting + +Markers combine freely. Wrap one inside another to stack effects: + +``` +**bold [#ff0000]and red[/]** +``` + +renders as bold text where the words "and red" are also red. + +## Color + +Color tags take a hex value only — 6 digits (`[#ff0000]`) or the 3-digit +shorthand (`[#f00]`, same as `#ff0000`). There's no named-color list to +memorize; if you know the hex, you can use it. `[/]` closes the nearest +still-open color (or weight) tag. + +## Explicit weight + +`**bold**` is really just shorthand for `[w700]bold[/]` — the conventional +"bold" weight. If you want something lighter or heavier than that, set the +weight directly: any number from 1 to 1000, e.g. `[w300]` for a light weight +or `[w900]` for a very heavy one. Whether the difference is visible depends +on the font — see the font weight note in the +[prefab scripting guide](prefab_scripting_guide.md#text-fields) for how that +works. + +## Superscript and subscript + +`^` raises a bit of text, `~` lowers it — handy for ordinals (`24^th^ Street`), +footnote marks (`Baker Street^*^`), and the occasional formula (`H~2~O`). The +raised text shrinks and shifts automatically; you don't set a size for it. + +These two have one extra rule the other markers don't: **no spaces inside**. +`^st^` works, `^a b^` doesn't. That's deliberate — carets and tildes turn up in +ordinary label text ("Terminal 2 ^ 3", "~5 mins to the pier"), and without the +rule every stray one would start swallowing words. If there's a space in +between, it's treated as plain text and left alone. + +Note that `~~` is still strikethrough. A single `~` is a subscript, a double +`~~` strikes through — the same way `*` is italic and `**` is bold. + +## Unclosed or malformed markers + +If a marker never finds its match — a `**` with no closing `**` before the +text ends, a `[#ff0000]` with no `[/]` — it's left as plain text, asterisks, +brackets and all, rather than swallowing the rest of the label or breaking +the render. A typo shouldn't eat your text. + +## Glyph shortcodes still work + +If your map has custom glyphs imported, `:alias:` shortcodes work exactly as +before, and can appear inside or alongside markdown: + +``` +**Oxford Circus :interchange:** +``` + +## Where it applies + +- Station titles and subtitles +- Free-standing text box objects (the "text" tool) + +It works the same way whether you're typing directly into a text field on +the canvas, or setting `text.text` from a +[prefab script](prefab_scripting_guide.md) — the markup lives in the string +itself either way. diff --git a/Prefab-Scripting-Guide.md b/Prefab-Scripting-Guide.md new file mode 100644 index 0000000..343ab7f --- /dev/null +++ b/Prefab-Scripting-Guide.md @@ -0,0 +1,471 @@ +# Writing Prefab Scripts + +Scripted prefabs let you generate the contents of a prefab from a small Lua +script instead of hand-drawing everything once and copy-pasting it around the +map. Declare a few parameters at the top of your script, then use those +parameters to draw boxes and text labels. Every time you place a new instance +of the prefab, change one of its parameters, or edit the script itself, the +script re-runs and the instance's content is regenerated from scratch. + +A quick note before you dive in: the editor UI for attaching a script to a +prefab and editing it in-app isn't wired up yet. This guide documents the +scripting language itself — the part that already works under the hood — +so you know what to write once that UI lands. + +## What a script actually does + +A prefab script is a normal Lua script. There's no `generate()` function or +special entry point — the whole file just runs top to bottom, every time. +Along the way it can: + +1. Declare the parameters the prefab exposes (`declareParams{...}`). +2. Read the current values of those parameters (`params..value`). +3. Create canvas objects (`CanvasObject.new(...)`) and set their fields. + +Whatever objects your script creates during that run become the instance's +content. There's no separate "commit" step — creating an object registers it +immediately. + +The script re-runs automatically: + +- When a new instance of the prefab is placed. +- Whenever that instance's parameter values change. +- Whenever the script's source is edited — this regenerates **every** + existing instance of the prefab, not just new ones. + +## Declaring parameters + +Parameters are declared once, with a call to `declareParams`: + +```lua +params = declareParams{ + { name = "label_text", type = "string", default = "Interchange" }, +} +``` + +Note it's a real function call (`declareParams{...}`, Lua's sugar for +`declareParams({...})`), not a bare table you assign to `params` yourself. +The table you pass in is a plain ordered list of field descriptors. What +`declareParams` hands back is different — a table keyed by name, so the rest +of your script reads values as `params.label_text.value`, not by position. + +Five field types are supported, all flat — there's no `list` or `object` +type, so a param can't nest another param inside it: + +| `type` | Extra keys | What `.value` looks like in Lua | +|---|---|---| +| `"string"` | `default` | a string | +| `"number"` | `default`, `min`, `max`, `decimals` | a number | +| `"boolean"` | `default` | `true` / `false` | +| `"color"` | `default` (a hex string) | a hex string, e.g. `"#da291c"` | +| `"select"` | `options` (a list of strings), `default` | a string, always one of `options` | + +Every field descriptor also takes an optional `section` — a plain string. +Fields sharing the same `section` get grouped into one collapsible section in +the properties panel, in the order they first appear; a field with no +`section` just renders as its own row. There's currently no way to declare a +variable-length repeated row of fields where the row *contents* are +user-authored per instance (the old `list`-of-`object` pattern) — that +capability was dropped along with `list`/`object` and hasn't been replaced +yet. If the row's length and contents instead come from existing map data — +one card per metro line, say — you don't need a declared list at all: loop +over `Lines.all` directly (see "Reading metro lines" below and Full example +2). + +**Naming convention:** capitalize `name` and `section` values — +`Label_Text` rather than `label_text`, `"Appearance"` rather than +`"appearance"`. `name` still doubles as the Lua accessor key +(`params.Label_Text.value`), so keep underscores instead of spaces there; +`section` is never accessed from Lua, so a plain title-case phrase like +`"Route Colors"` is fine. + +### Select + +`select` is a string param constrained to a fixed set of choices, rendered as +a dropdown in the properties panel instead of a free text field: + +```lua +params = declareParams{ + { name = "shape", type = "select", options = {"circle", "square", "diamond"}, default = "circle" }, +} +``` + +`options` is required and can't be empty — a script that declares a `select` +param without one fails the whole run, same as any other bad declaration. +`params.shape.value` reads back as a plain string, exactly like `string`. If +a `default` isn't one of `options` (or is missing), the value falls back to +the first entry in `options`. + +`min`, `max`, and `decimals` on a `number` param are just metadata describing +how the value should be presented and edited — the script engine doesn't +clamp anything to them itself. + +### Colors + +Color values are always plain hex strings, both when you declare a default +and when you read `params..value` back in your script — `"#DA291C"`. +6 digits means fully opaque (`RRGGBB`); if you need to control opacity too +you can use 8 digits, alpha first (`AARRGGBB`), e.g. `"#80DA291C"`. + +### `list` and `object` — nesting + +`list` needs an `item` describing the schema of every element. `object` +needs a `fields` list, and each entry in that list is itself a full field +descriptor (name + type + whatever else that type needs) — which means a +`list` of `object`s, or an `object` with a `list` field, or deeper, all just +fall out of the same recursive shape. This is what a "row of colored route +badges" — a list of `{number, color}` pairs — looks like declared: + +```lua +params = declareParams{ + { name = "routes", type = "list", item = { + type = "object", + fields = { + { name = "number", type = "string" }, + { name = "color", type = "color" }, + } + } + }, +} +``` + +Reading it back is a normal Lua loop over a table of tables: + +```lua +for i, route in ipairs(params.routes.value) do + -- route.number is a string, route.color is a hex string +end +``` + +## Creating canvas objects + +```lua +local box = CanvasObject.new("box", { + x = -60, + y = -20, + width = 120, + height = 40, +}) +``` + +`CanvasObject.new(kind, fields)` creates an object and optionally applies a +table of fields immediately. You can also set or change fields afterward: + +```lua +local label = CanvasObject.new("text") +label.text = "Central" +label.textAlign = TextAlign.center +``` + +Right now only two kinds are supported: + +### Shared fields + +Every generated canvas object supports the same placement fields: + +| Field | Meaning | Default if unset | +|---|---|---| +| `name` | object name | `""` | +| `x`, `y` | position, relative to the instance's anchor | `0`, `0` | +| `width`, `height` | layout box size | kind-specific | +| `anchorX`, `anchorY` | where the position sits inside the box, `0`-`1` | `0.5`, `0.5` | +| `lockAspectRatio` | preserve width:height while resizing | `false` | +| `rotation` | rotation, radians | `0` | +| `opacity` | `0`-`1` | `1` | +| `zIndex` | stacking order | `0` | + +### `"box"` fields + +| Field | Meaning | Default if unset | +|---|---|---| +| `width`, `height` | size | `160`, `80` | +| `cornerRadius` | corner rounding | `8` | +| `fillColor` | fill color, hex string | `"#ffffff"` | +| `color` | alias for `fillColor` | `"#ffffff"` | +| `borderColor` | border color, hex string | `"#333333"` | +| `borderWidth` | border thickness | `2` | + +### `"text"` fields + +| Field | Meaning | Default if unset | +|---|---|---| +| `width`, `height` | text layout box size | `160`, `40` | +| `text` | the label's text | `"Label"` | +| `fontFamily` | font family name | app default | +| `fontSize` | font size | `11` | +| `color` | text color, hex string | `"#444466"` | +| `textAlign` | `TextAlign.left`, `TextAlign.center`, `TextAlign.right`, etc. | `TextAlign.center` | +| `verticalAlign` | `TextVerticalAlign.top`, `TextVerticalAlign.center`, `TextVerticalAlign.bottom` | `TextVerticalAlign.top` | +| `labelPadding` | draw the white label halo behind text | `false` | +| `padding` | inset the text inside its layout box, an `EdgeInsets` (see below) | none | + +Any field you don't set just falls back to that default — you never have to +set every field on every object. + +`TextAlign` and `TextVerticalAlign` are declared enum tables. Use those enum +values instead of raw strings; an invalid enum value fails the whole +generation attempt so typos don't silently render with the wrong alignment. + +### `EdgeInsets` — padding + +`padding` doesn't take a bare number or a loose table of sides; it takes an +`EdgeInsets`, built with `EdgeInsets.new`. There are two ways to call it, and +any side you leave out is `0`: + +```lua +EdgeInsets.new(20, 3, 0, 0) -- left, top, right, bottom +EdgeInsets.new{ left = 20, top = 3 } -- name only the sides you want +``` + +so a cell label inset 20 from the left edge of its box is: + +```lua +CanvasObject.new("text", { + x = colX, + y = rowY, + width = colW, + height = rowH, + text = "7-13", + padding = EdgeInsets.new{ left = 20 }, +}) +``` + +Padding insets the text *within* the layout box you gave it — the box stays +where `x`/`y`/`width`/`height` put it. That matters when you're rebuilding a +hand-drawn panel: labels drawn in the editor usually carry their own padding +(a cell label is typically `left = 14`, `top = 14`), and `get_properties` +reports it, so you can copy those numbers across rather than trying to fake +the inset by nudging `x`. + +Like the enum tables, `EdgeInsets.new` is strict: an unknown side name or a +non-number value fails the whole generation attempt rather than silently +resolving to zero. + +There's no separate `fontWeight`/`italic` field anymore — bold, italic, +underline, strikethrough, inline color and explicit weight are all written +directly into `text` as micro-markdown instead, e.g. +`text.text = "**Oxford Circus** [w300]zone 1[/]"`. See +[Micro Markdown](micro_markdown_guide.md) for the full syntax. + +You can also read fields back off an object you created earlier in the same +script (e.g. `box.width * 2`) — property reads and writes both go through +the same object, so this works fine, it's just not typically needed. + +### Coordinate space + +`x`/`y` on every object are **local to wherever the prefab instance gets +placed** — `(0, 0)` is the instance's own anchor point, not a fixed spot on +the map. The app adds the instance's actual position on top after your +script runs, so you never need to know or care where on the map the instance +actually ended up while you're writing the script. + +## Instance geometry + +Every instance also has its own width/height and anchor point — the same +placement box you'd get on any other canvas object, editable directly in its +properties panel. Inside a script, this is a plain global table called +`instance`: + +```lua +box.width = instance.width +box.height = instance.height +box.anchorX = instance.anchorX +box.anchorY = instance.anchorY +``` + +- **Reading** `instance.width`/`instance.height` gives you the instance's + current stored size, so you can lay your generated content out relative to + it instead of hardcoding numbers. +- **Reading** `instance.anchorX`/`instance.anchorY` gives you the instance's + current anchor fractions, where `0.5, 0.5` is centered, `0, 0` is top-left, + and `1, 1` is bottom-right. +- **Writing** `instance.width`/`instance.height` during your script lets you + report a size back that the script computed itself — handy for anything + whose natural footprint depends on its parameters (a variable-length row of + items, say) rather than being fixed. Whatever you leave those fields as + when the script finishes is what gets saved as the instance's size. +- **Writing** `instance.anchorX`/`instance.anchorY` works the same way: the + values left at the end of the script are saved as the instance's anchor. +- If your script never touches `instance` at all, nothing changes — same + value in, same value out. + +## Reading metro lines + +Scripts get read-only access to the map's metro lines (the same lines you +see and edit in the Lines panel) through a global called `Lines`: + +```lua +for i, line in ipairs(Lines.all) do + -- line.id, line.name, line.color, line.style, line.width, line.padding +end + +local central = Lines.byId(3) +if central then + box.color = central.color +end +``` + +- `Lines.all` is every line on the map, in order, as a normal 1-indexed + table. `Lines.byId(id)` looks one up directly and gives you `nil` if that + id doesn't exist — handy for a prefab whose parameters reference a + specific line by id rather than looping over all of them. +- Each line's table has `id`, `name`, `color` (a hex string, same as every + other color field in this API), `style` (a string — `"single"`, + `"doubleTrack"`, `"road"`, or `"dashed"`), `width`, and `padding`. +- This is a snapshot handed to your script fresh every run — changing a + field on a table you got from `Lines` (`line.name = "whatever"`) only + affects your own local copy, it never writes back to the real map. +- It only covers line attributes, not which stations or segments belong to + a line — that's not available to scripts yet. + +## Measuring text + +`measureText(text, options)` tells you how much space a single line of text +would take up, without creating a `"text"` object for it — useful for sizing +a box around a label before you commit to laying it out: + +```lua +local size = measureText("7-13", { fontFamily = "Inter", fontSize = 24 }) +-- size.width, size.height + +local badge = CanvasObject.new("box", { + x = 0, y = 0, + width = size.width + 20, -- pad the box out around the measured text + height = size.height + 10, +}) +``` + +`options` is optional and takes the same font-ish fields a `"text"` object +does: + +| Option | Meaning | Default if unset | +|---|---|---| +| `fontFamily` | font family name, or a custom font id off `CustomFonts` | app default | +| `fontSize` | font size | `11` | +| `weight` | font weight (`100`-`900`) | `400` | + +An unknown option name fails the whole generation attempt, same as +`EdgeInsets.new`. This only measures a *single line* — it doesn't wrap, so +for multi-line text you'd measure a representative line rather than the +whole string. + +## What's off-limits right now + +- **Only `"box"` and `"text"` kinds exist.** `CanvasObject.new("image")` (or + anything else) fails — and because a script's output is all-or-nothing, + one bad object call fails the *entire* instance, not just that one object. +- **No way to read stations, segments, or other canvas objects from a + script.** A `canvas` global for reading the rest of the scene tree + (station names, other images, etc.) is planned but not available yet — + scripts can read metro line info (see above) and create new objects, but + can't inspect or react to anything else in the map. +- **Sandboxed.** No file access, no network access, no `os`, `io`, + `require`, `load`/`loadfile`/`dofile`, and no `coroutine`. The base Lua + library plus `math`, `string`, and `table` are all available, so ordinary + logic (loops, string formatting, `math.floor`, etc.) works normally, with + one exception: `#` gives you the length of a **table** only. On a string it + throws ("attempt to get length of a string value") and, because a failed run + produces nothing, takes the whole instance down with it. Use `string.len(s)` + or `s:len()` instead. +- **Bounded execution.** A script gets a time budget (2 seconds) and an + object-count cap (2000 objects) per run. A runaway loop trips one of these + and fails the run with an explanatory message rather than hanging. + +## When something goes wrong + +If your script has a syntax error, throws a runtime error (e.g. indexing +`nil`), creates an unsupported object kind, or blows past the time/object +budget, the whole generation attempt fails: the instance ends up with **no** +generated content for that run (whatever it had before is cleared, nothing +new takes its place), and the error message is kept on the instance so it's +visible while you're fixing the script. The error is also always printed to +the console. There's no partial output — either the script runs clean start +to finish, or the instance produces nothing. + +## Full example 1 — a single labeled box + +A minimal interchange badge: one box with a text label centered on it. + +```lua +params = declareParams{ + { name = "Label_Text", type = "string", default = "Interchange", section = "Text" }, +} + +local box = CanvasObject.new("box") +box.x = -60 +box.y = -20 +box.width = 120 +box.height = 40 +box.cornerRadius = 6 +box.color = "#ffffff" +box.borderColor = "#333333" +box.borderWidth = 2 + +local label = CanvasObject.new("text") +label.x = 0 +label.y = 0 +label.text = "**" .. params.Label_Text.value .. "**" +label.fontSize = 14 +``` + +Change the `Label_Text` parameter on an instance and only that instance's +label text changes — the box shape stays as authored in the script. + +## Full example 2 — a row of route cards + +A variable-length row without a declared `list` param at all — the row's +length and content come from `Lines.all` (every metro line on the map, see +"Reading metro lines" above) instead. The card row resizes to fit the +instance's own width, splitting it evenly across however many lines exist: + +```lua +params = declareParams{ + { name = "Gap", type = "number", default = 11, min = 0, decimals = 0 }, +} + +local cardHeight = instance.height +local gap = params.Gap.value +local routeCount = #Lines.all +local cardWidth = 0 + +if routeCount > 0 then + cardWidth = (instance.width - gap * (routeCount - 1)) / routeCount +end + +for i, line in ipairs(Lines.all) do + local routeNumber = string.gsub(line.name, "^Route ", "") + local x = (i - 1) * (cardWidth + gap) + + CanvasObject.new("box", { + x = x, + y = 0, + width = cardWidth, + height = cardHeight, + anchorX = 0, + anchorY = 0, + color = line.color, + }) + + CanvasObject.new("text", { + x = x, + y = 0, + width = cardWidth, + height = cardHeight, + anchorX = 0, + anchorY = 0, + text = "**" .. routeNumber .. "**", + fontFamily = "Inter", + fontSize = 42, + color = "#FFFFFF", + textAlign = TextAlign.center, + verticalAlign = TextVerticalAlign.center, + }) +end + +instance.anchorX = 0 +instance.anchorY = 0 +``` + +Add or remove a line on the map and the card row grows or shrinks +automatically, no instance param edit needed — the only declared param here +(`Gap`) controls spacing, not row count. diff --git a/Privacy-Policy.md b/Privacy-Policy.md index 7b02071..99225ea 100644 --- a/Privacy-Policy.md +++ b/Privacy-Policy.md @@ -1,82 +1,99 @@ -# Arcs & Angles Privacy Policy - -**Effective Date:** March 6, 2026 -**Last Updated:** March 6, 2026 - -This Privacy Policy explains how IMBENJI.NET LTD ("we", "us", "our") handles information when you use Arcs & Angles (the "Service"). - -## 1. Scope - -This policy applies to the Arcs & Angles web and desktop applications and related features. - -## 2. Information We Process - -Depending on how you use the Service, we may process: - -- Content you create in the app (for example map files, labels, and exports). -- Technical and usage data needed to run core features (for example update checks and network requests required for app functionality). -- Local settings and recovery data stored on your device (for example preferences, recent files, and recovery drafts). -- Account or backend-related data processed through Supabase when those features are enabled (for example authentication identifiers and related metadata). - -## 3. Local Storage - -Arcs & Angles stores certain data locally on your device to provide core features, including save/recovery and app preferences. - -You control your local files and may delete them from your device at any time. - -## 4. Third-Party Services (Including Supabase) - -The Service uses third-party services to support functionality, including Supabase for backend services and database/authentication capabilities, plus external links and font delivery for exports. - -Those services are governed by their own privacy policies and terms. - -## 5. How We Use Information - -We use information to: - -- Provide and operate the Service. -- Maintain reliability, security, and performance. -- Support export, update, and recovery functionality. -- Improve and develop Service features. - -## 6. Sharing - -We do not sell personal information. - -We may share information only when necessary to operate the Service (including with Supabase as our backend processor/provider), comply with legal obligations, enforce our terms, or protect rights and safety. - -## 7. Data Retention - -Retention depends on the data type and purpose. - -Local app data remains on your device until removed, overwritten, or cleared by app actions. - -## 8. Security - -We use reasonable technical and organizational measures to protect information we process. No method of transmission or storage is completely secure. - -## 9. Children's Privacy - -The Service is not directed to children under 13. If we become aware that personal data from a child has been collected in violation of applicable law, we will take appropriate steps to address it. - -## 10. Your Rights - -Depending on your location, you may have rights regarding your personal data, including access, correction, deletion, or objection. - -To exercise rights requests, contact us using the details below. - -## 11. International Users - -If you access the Service from outside the United Kingdom, you understand your information may be processed in jurisdictions where we or our providers operate. - -## 12. Changes to This Policy - -We may update this Privacy Policy from time to time. Material changes may be communicated through the Service or other reasonable means. - -## 13. Contact - -For privacy questions or requests, contact: - -- **Legal Entity:** IMBENJI.NET LTD -- **Email:** benjamin.watt@imbenji.net -- **Address:** IMBENJI.NET LTD, 127 Ching Way, London, E4 8YE, United Kingdom +# Arcs & Angles Privacy Policy + +**Effective Date:** March 6, 2026 +**Last Updated:** August 16, 2026 + +This Privacy Policy explains how IMBENJI.NET LTD ("we", "us", "our") handles information when you use Arcs & Angles (the "Service"). + +## 1. Scope + +This policy applies to the Arcs & Angles web and desktop applications and related features. + +## 2. Information We Process + +Depending on how you use the Service, we may process: + +- Content you create in the app (for example map files, labels, and exports). +- Technical and usage data needed to run core features (for example IP address, device/browser information, request timestamps, and application version checks). +- Local settings and recovery data stored on your device (for example preferences, recent files, and recovery drafts). +- Account or backend-related data processed through our backend services when those features are enabled (for example authentication identifiers and related metadata). This authentication infrastructure is operated by IMBENJI.NET LTD, not a third party. +- Content you send to or generate through the AI Assistant/agent feature when you use it (for example your prompts, relevant map content, and the Assistant's responses), routed through our backend to OpenRouter, our third-party AI model provider, under a zero data retention configuration (see Section 5). + +## 3. Legal Bases for Processing (UK GDPR) + +Where UK GDPR applies, we process personal data under one or more of the following legal bases: + +- **Contract:** where processing is necessary to provide the Service you request. +- **Legitimate Interests:** where processing is necessary for service security, reliability, abuse prevention, and product improvement, and does not override your rights. +- **Legal Obligation:** where processing is needed to comply with applicable law. +- **Consent:** where required by law, including where you choose to enable optional features. + +## 4. Local Storage + +Arcs & Angles stores certain data locally on your device to provide core features, including save/recovery and app preferences. + +You control your local files and may delete them from your device at any time. + +## 5. Third-Party Services + +The Service uses third-party services to support functionality, including external links and font delivery for exports. + +The optional AI Assistant/agent feature is powered by OpenRouter, a third-party AI model routing provider. We use OpenRouter with zero data retention (ZDR) enabled: for inference requests, OpenRouter routes only to model providers that do not store or train on your data. This ZDR guarantee covers the core chat/inference requests but does not extend to optional plugins or tools (for example, the Assistant's web search capability), which may be subject to those tools' own data handling. Account authentication and other backend services are operated directly by IMBENJI.NET LTD and are not third-party processing. + +Those services are governed by their own privacy policies and terms. + +## 6. How We Use Information + +We use information to: + +- Provide and operate the Service. +- Maintain reliability, security, and performance. +- Support export, update, and recovery functionality. +- Improve and develop Service features. +- Improve the Service, including the AI Assistant/agent feature, using information from how the Assistant/agent is used. + +## 7. International Data Transfers + +If personal data is transferred outside the United Kingdom, we use appropriate safeguards required by applicable law, which may include adequacy decisions or approved standard contractual clauses. + +## 8. Sharing + +We do not sell personal information. + +We may share information only when necessary to operate the Service (including with third-party providers such as font delivery), comply with legal obligations, enforce our terms, or protect rights and safety. Processing by our own backend and authentication infrastructure (operated by IMBENJI.NET LTD) is not third-party sharing. + +## 9. Data Retention + +Retention depends on the data type and purpose. + +Local app data remains on your device until removed, overwritten, or cleared by app actions. + +## 10. Security + +We use reasonable technical and organizational measures to protect information we process. No method of transmission or storage is completely secure. + +## 11. Children's Privacy + +The Service is not directed to children under 13. If we become aware that personal data from a child has been collected in violation of applicable law, we will take appropriate steps to address it. + +## 12. Your Rights + +Depending on your location, you may have rights regarding your personal data, including access, correction, deletion, or objection. + +To exercise rights requests, contact us using the details below with the subject line `Privacy Request` and a description of your request. + +## 13. Cookies and Similar Technologies + +The Service may use essential local storage or equivalent browser/device storage to support core app features (for example preferences and recovery behavior). We do not use advertising cookies. + +## 14. Changes to This Policy + +We may update this Privacy Policy from time to time. Material changes may be communicated through the Service or other reasonable means. + +## 15. Contact + +For privacy questions or requests, contact: + +- **Legal Entity:** IMBENJI.NET LTD +- **Email:** benjamin.watt@imbenji.net +- **Address:** IMBENJI.NET LTD, 127 Ching Way, London, E4 8YE, United Kingdom diff --git a/Terms-of-Service.md b/Terms-of-Service.md index 6e40712..3847bdf 100644 --- a/Terms-of-Service.md +++ b/Terms-of-Service.md @@ -1,131 +1,137 @@ -# Arcs & Angles Terms of Service - -**Effective Date:** March 6, 2026 -**Last Updated:** March 6, 2026 - -These Terms of Service ("Terms") govern your access to and use of Arcs & Angles (the "Service"), including our web app, desktop app, and related features. - -By accessing or using the Service, you agree to these Terms. If you do not agree, do not use the Service. - -## 1. Eligibility - -You must be at least 13 years old (or the minimum age required in your jurisdiction) to use the Service. - -If you use the Service on behalf of an organization, you represent that you have authority to bind that organization to these Terms. - -## 2. The Service - -Arcs & Angles is a design tool for creating stylized metro/subway maps. - -Current and planned features may include local map editing, save/load, import/export, recovery features, and optional integrations with third-party services. - -We may add, remove, or change features at any time. - -## 3. Alpha Status and Availability - -The Service may be offered in alpha, beta, or other pre-release states. - -Pre-release software may contain bugs, incomplete features, and compatibility issues. You use the Service at your own risk. - -We do not guarantee uninterrupted availability, error-free operation, or that any specific feature will remain available. - -## 4. User Content and Ownership - -"User Content" means files, map data, text, and other content you create, import, upload, or export through the Service. - -You retain ownership of your User Content. - -By using the free tier, you grant IMBENJI.NET LTD a worldwide, non-exclusive, royalty-free, transferable, sublicensable license to use, reproduce, adapt, publish, distribute, and display your User Content for operating, improving, and promoting the Service. - -If you are on the free tier, you may create and redistribute your User Content, but you may not sell, license for a fee, or otherwise commercially monetize that User Content unless you have an active paid plan or separate written commercial permission from IMBENJI.NET LTD. - -## 5. Local Storage, Recovery, and Data Handling - -The Service may store certain data locally on your device (for example, recent files, preferences, and recovery/session drafts) to support functionality. - -You are responsible for managing your own backups and exported files. Recovery features are best-effort and may fail. - -We are not responsible for loss or corruption of local files, drafts, or unsaved work. - -## 6. Third-Party Services and Links - -The Service may rely on or link to third-party services (for example, font delivery, external issue/release pages, optional backend services). - -Your use of third-party services is subject to those providers' own terms and privacy policies. We are not responsible for third-party services. - -## 7. Acceptable Use - -You agree not to: - -- Use the Service in violation of any law or regulation. -- Infringe, misappropriate, or violate others' intellectual property or other rights. -- Interfere with or disrupt the Service, including attempts to bypass security controls. -- Upload, distribute, or create content that is unlawful, fraudulent, or malicious. -- Reverse engineer or attempt to extract source code except where prohibited by law. -- Remove, obscure, alter, or circumvent watermarks, attribution notices, or rights-management mechanisms applied by the Service. - -## 8. Intellectual Property - -The Service, including software, design, branding, and documentation (excluding your User Content), is owned by or licensed to us and protected by intellectual property laws. - -No rights are granted except as expressly stated in these Terms. - -## 9. Payments and Commercial Terms - -If paid plans, subscriptions, or paid features are offered, additional commercial terms may apply at purchase. - -Nothing in this section limits any mandatory cancellation, refund, or other consumer rights you may have under applicable law. - -## 10. Suspension and Termination - -We may suspend or terminate your access to the Service at any time, with or without notice, including if we believe you violated these Terms or created risk for the Service or others. - -Violations involving watermark circumvention or unauthorized commercial use on the free tier may result in immediate suspension or termination and, where appropriate, legal action. - -You may stop using the Service at any time. - -Sections that by their nature should survive termination will survive (including ownership, disclaimers, limitation of liability, and dispute terms). - -## 11. Disclaimer of Warranties - -TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE," WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND QUIET ENJOYMENT. - -IF YOU ARE A CONSUMER, NOTHING IN THESE TERMS EXCLUDES RIGHTS THAT CANNOT BE EXCLUDED UNDER APPLICABLE LAW, INCLUDING THE CONSUMER RIGHTS ACT 2015. - -## 12. Limitation of Liability - -TO THE MAXIMUM EXTENT PERMITTED BY LAW, WE WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOSS OF PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS INTERRUPTION, ARISING OUT OF OR RELATED TO YOUR USE OF THE SERVICE. - -TO THE MAXIMUM EXTENT PERMITTED BY LAW, OUR TOTAL LIABILITY FOR ALL CLAIMS RELATING TO THE SERVICE WILL NOT EXCEED THE GREATER OF (A) THE AMOUNT YOU PAID US FOR THE SERVICE IN THE 12 MONTHS BEFORE THE CLAIM OR (B) GBP 50. - -NOTHING IN THESE TERMS EXCLUDES OR LIMITS LIABILITY FOR: (A) DEATH OR PERSONAL INJURY CAUSED BY NEGLIGENCE; (B) FRAUD OR FRAUDULENT MISREPRESENTATION; OR (C) ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW. - -## 13. Indemnification - -If you are using the Service for business purposes, you agree to indemnify and hold harmless IMBENJI.NET LTD, its affiliates, officers, directors, employees, and agents from and against claims, liabilities, damages, losses, and expenses (including reasonable legal fees) arising from your use of the Service, your User Content, or your violation of these Terms. - -## 14. Governing Law and Disputes - -These Terms are governed by the laws of England and Wales, without regard to conflict-of-laws principles. - -If you are a business user, you agree that disputes will be resolved exclusively in the courts of England and Wales. - -If you are a consumer, you may bring claims in the courts of England and Wales or, where required by law, in the courts of the part of the United Kingdom where you live. - -## 15. Changes to These Terms - -We may update these Terms from time to time. - -If we make material changes, we may provide notice through the Service or other reasonable means. Your continued use after the effective date of updated Terms constitutes acceptance. - -## 16. Contact - -For legal notices or questions about these Terms, contact: - -- **Legal Entity:** IMBENJI.NET LTD -- **Email:** benjamin.watt@imbenji.net -- **Address:** IMBENJI.NET LTD, 127 Ching Way, London, E4 8YE, United Kingdom - -## 17. Open Source Notices - -The Service may include open source components licensed under separate terms. Those licenses apply to the extent required by their terms. +# Arcs & Angles Terms of Service + +**Effective Date:** March 6, 2026 +**Last Updated:** August 16, 2026 + +These Terms of Service ("Terms") govern your access to and use of Arcs & Angles (the "Service"), including our web app, desktop app, and related features. + +By accessing or using the Service, you agree to these Terms. If you do not agree, do not use the Service. + +## 1. Eligibility + +You must be at least 13 years old (or the minimum age required in your jurisdiction) to use the Service. + +If you use the Service on behalf of an organization, you represent that you have authority to bind that organization to these Terms. + +## 2. The Service + +Arcs & Angles is a design tool for creating stylized metro/subway maps. + +Current and planned features may include local map editing, save/load, import/export, recovery features, an optional AI Assistant/agent feature, and integrations with backend services operated by IMBENJI.NET LTD (including account authentication). + +We may add, remove, or change features at any time. + +## 3. Alpha Status and Availability + +The Service may be offered in alpha, beta, or other pre-release states. + +Pre-release software may contain bugs, incomplete features, and compatibility issues. You use the Service at your own risk. + +We do not guarantee uninterrupted availability, error-free operation, or that any specific feature will remain available. + +## 4. User Content and Ownership + +"User Content" means files, map data, text, and other content you create, import, upload, or export through the Service. + +You retain ownership of your User Content. + +By using the free tier, you grant IMBENJI.NET LTD a worldwide, non-exclusive, royalty-free, transferable, sublicensable license to use, reproduce, adapt, publish, distribute, and display your User Content solely as needed to operate, improve, and promote the Service. + +If you are on the free tier, you may create and redistribute your User Content, but you may not sell, license for a fee, or otherwise commercially monetize that User Content unless you have an active paid plan or separate written commercial permission from IMBENJI.NET LTD. This includes, for example, selling map files, selling prints or merchandise using exported maps, or bundling maps into paid products or services. + +You may not create, distribute, or sell templates, asset packs, editable project files, or similar resources whose primary purpose is to enable third parties to create maps using the Service without requiring them to obtain their own license to the Service. + +## 5. Local Storage, Recovery, and Data Handling + +The Service may store certain data locally on your device (for example, recent files, preferences, and recovery/session drafts) to support functionality. + +You are responsible for managing your own backups and exported files. Recovery features are best-effort and may fail. + +We are not responsible for loss or corruption of local files, drafts, or unsaved work. + +## 6. Third-Party Services and Links + +The Service may rely on or link to third-party services (for example, font delivery and external issue/release pages). Account authentication is handled by backend infrastructure operated by IMBENJI.NET LTD, not a third party. + +The optional AI Assistant/agent feature is powered by OpenRouter, a third-party AI model routing provider, used with zero data retention (ZDR) enabled for inference requests. This does not extend to optional plugins or tools available through the Assistant (for example, web search), which may be subject to those tools' own data handling. Do not submit content through the AI Assistant/agent feature that you are not permitted to share with a third-party AI provider. + +Your use of third-party services is subject to those providers' own terms and privacy policies. We are not responsible for third-party services, including outputs generated by the AI Assistant/agent feature. + +## 7. Acceptable Use + +You agree not to: + +- Use the Service in violation of any law or regulation. +- Infringe, misappropriate, or violate others' intellectual property or other rights. +- Interfere with or disrupt the Service, including attempts to bypass security controls. +- Upload, distribute, or create content that is unlawful, fraudulent, or malicious. +- Reverse engineer or attempt to extract source code except where prohibited by law. +- Remove, obscure, alter, or circumvent watermarks, attribution notices, or rights-management mechanisms applied by the Service. + +## 8. Intellectual Property + +The Service, including software, design, branding, and documentation (excluding your User Content), is owned by or licensed to us and protected by intellectual property laws. + +No rights are granted except as expressly stated in these Terms. + +If you believe content available through the Service infringes your copyright, contact `benjamin.watt@imbenji.net` with sufficient detail for us to review and respond. + +## 9. Payments and Commercial Terms + +If paid plans, subscriptions, or paid features are offered, additional commercial terms may apply at purchase. + +Nothing in this section limits any mandatory cancellation, refund, or other consumer rights you may have under applicable law. + +## 10. Suspension and Termination + +We may suspend or terminate your access to the Service at any time, with or without notice, including if we believe you violated these Terms or created risk for the Service or others. + +Violations involving watermark circumvention or unauthorized commercial use on the free tier may result in immediate suspension or termination and, where appropriate, legal action. + +You may stop using the Service at any time. + +Sections that by their nature should survive termination will survive (including ownership, disclaimers, limitation of liability, and dispute terms). + +## 11. Disclaimer of Warranties + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE," WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND QUIET ENJOYMENT. + +IF YOU ARE A CONSUMER, NOTHING IN THESE TERMS EXCLUDES RIGHTS THAT CANNOT BE EXCLUDED UNDER APPLICABLE LAW, INCLUDING THE CONSUMER RIGHTS ACT 2015. + +## 12. Limitation of Liability + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, WE WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOSS OF PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS INTERRUPTION, ARISING OUT OF OR RELATED TO YOUR USE OF THE SERVICE. + +TO THE MAXIMUM EXTENT PERMITTED BY LAW, OUR TOTAL LIABILITY FOR ALL CLAIMS RELATING TO THE SERVICE WILL NOT EXCEED THE GREATER OF (A) THE AMOUNT YOU PAID US FOR THE SERVICE IN THE 12 MONTHS BEFORE THE CLAIM OR (B) GBP 50. + +NOTHING IN THESE TERMS EXCLUDES OR LIMITS LIABILITY FOR: (A) DEATH OR PERSONAL INJURY CAUSED BY NEGLIGENCE; (B) FRAUD OR FRAUDULENT MISREPRESENTATION; OR (C) ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW. + +## 13. Indemnification + +If you are using the Service for business purposes, you agree to indemnify and hold harmless IMBENJI.NET LTD, its affiliates, officers, directors, employees, and agents from and against claims, liabilities, damages, losses, and expenses (including reasonable legal fees) arising from your use of the Service, your User Content, or your violation of these Terms. + +## 14. Governing Law and Disputes + +These Terms are governed by the laws of England and Wales, without regard to conflict-of-laws principles. + +If you are a business user, you agree that disputes will be resolved exclusively in the courts of England and Wales. + +If you are a consumer, you may bring claims in the courts of England and Wales or, where required by law, in the courts of the part of the United Kingdom where you live. + +## 15. Changes to These Terms + +We may update these Terms from time to time. + +If we make material changes, we may provide notice through the Service or other reasonable means. Your continued use after the effective date of updated Terms constitutes acceptance. + +## 16. Contact + +For legal notices or questions about these Terms, contact: + +- **Legal Entity:** IMBENJI.NET LTD +- **Email:** benjamin.watt@imbenji.net +- **Address:** IMBENJI.NET LTD, 127 Ching Way, London, E4 8YE, United Kingdom + +## 17. Open Source Notices + +The Service may include open source components licensed under separate terms. Those licenses apply to the extent required by their terms.