Skip to content

Latest commit

 

History

History
298 lines (225 loc) · 9.95 KB

File metadata and controls

298 lines (225 loc) · 9.95 KB

Getting Started with Synura Extensions

"Ecosystem, Developer Experience (DX) is everything."

This guide will walk you through creating your first Synura extension.

Data-Driven Views

Synura uses a Data-Driven architecture that inverts the traditional web development model:

Traditional (HTML) Synura
Write HTML structure first Write JavaScript data first
Add JS for interactivity Views render automatically
You control every pixel Strict, optimized view templates
Markup → Logic Data → Views

How it works:

  1. Your extension provides data (JavaScript objects)
  2. You specify a view type (list, post, chat, etc.)
  3. Synura renders the UI natively - no HTML, no CSS
// You write data, not markup
synura.open({
  view: '/views/list',
  models: {
    contents: [
      { title: "Article 1", author: "Alice", date: "Today" },
      { title: "Article 2", author: "Bob", date: "Yesterday" }
    ]
  }
});
// Synura renders a native list automatically

Benefits:

  • 🚀 Faster development - No UI code to write
  • 📱 Native performance - Views are optimized for each platform
  • 🎨 Consistent UX - All extensions look polished
  • 🔒 Secure - No DOM manipulation, no XSS risks

Development Tools

We provide a Visual Polyfill that simulates the Synura environment directly in your web browser. This allows you to develop and debug extensions using standard DevTools before testing them on the device. Enable Debug Mode to set breakpoints and step through your handler functions.

Prerequisites

  • A text editor (VS Code, Sublime Text, etc.).
  • The Synura application installed on your device.

Extension Structure

A Synura extension is a single JavaScript file that exports a SYNURA object containing metadata and the extension logic.

The SYNURA Object

The SYNURA object serves as the manifest and entry point for your extension.

var SYNURA = {
  name: "My First Extension",
  api: 0,
  version: 0.1,
  description: "A simple hello world extension.",
  license: "Apache-2.0",
  domain: "example.com",
  host_permissions: ["https://example.com/*"],
  icon: "emoji:📖",
  bypass: "firefox",
  main: {
    home: function() {},
    deeplink: function(url) {
      return true;
    },
    resume: function(viewId, context) {}
  }
};
  • domain: The extension identity and default network scope. If host_permissions is omitted, fetch can only use http://<domain>/* and https://<domain>/*.
  • host_permissions: Optional explicit network permissions for fetch. Use this when the site needs sibling subdomains or a narrower path scope, for example ["https://*.github.io/*"]. Patterns must stay in the same registrable domain family as domain; public registry wildcards such as *.io, *.com, and *.co.kr are rejected.
  • icon: The icon must be a URL. It can be an HTTP(S) URL to an image (hosted on the same domain) or an Emoji URL (e.g., emoji:📖). If omitted, https://<domain>/favicon.ico is used by default.

The main Object

The main object (inside SYNURA) must define a home function, which is the entry point when the user opens your extension.

main: {
  home: function() {},
  deeplink: function(url) {
    return true;
  },
  resume: function(viewId, context) {}
}

Deep Linking

You can enable deep linking to allow your extension to handle links matching your domain.

  1. Add deeplink: true to your SYNURA object.
  2. Implement the deeplink(url) function in your main object.
const SYNURA = {
  domain: "example.com",
  deeplink: true,
  main: {
    deeplink: function(url) {
      if (url.includes("/post/")) {
        synura.open("/views/post", {
          models: {
            link: url,
          }
        });
        return true;
      }
      return false;
    }
  }
};

Bookmarks

Synura supports bookmarking views. Users can browse their bookmarks, which display the cached state of the view (snapshot).

However, if the user wants to interact with the view again (restore it to a live runtime), they will tap the restore icon in the bookmark list. This action triggers the resume function in your extension instead of home.

Your extension must implement resume to re-attach event listeners to the restored view.

resume: function(viewId, context) {
  synura.connect(viewId, context, function(event) {
    if (event.eventId === "LOAD") {}
  });
}

Domain Launcher Support

You can allow users to install your extension by simply typing your website domain (e.g., https://example.com) into Synura.

  1. Host synura.js: Place your extension file at https://yourdomain.com/synura.js.
  2. Configure SYNURA.domain: Ensure the domain field in your SYNURA object matches your hosting domain.
const SYNURA = {
  name: "My Extension",
  domain: "yourdomain.com",
};
When a user enters `yourdomain.com` or `https://yourdomain.com`, Synura will automatically fetch `https://yourdomain.com/synura.js`, verify the domain match, and install the extension. If no protocol is provided, `https://` is used by default.

DOM API Support

CSS Selectors

The internal DOM engine supports the following CSS selectors for querySelector and querySelectorAll:

  • Tag Name: Selects elements by tag name (e.g., div, a).
  • Class: Selects elements by class name (e.g., .content, .active).
  • ID: Selects elements by ID (e.g., #main).
  • Attributes: Selects elements by attribute presence or value (e.g., [href], [data-type="post"]).
  • Compound: Combines tag, class, and ID (e.g., div.content, a#link.active).
  • Descendant: Selects nested elements (e.g., div p selects all <p> inside <div>).
  • Direct Child: Selects direct children (e.g., ul > li).

Element Methods

In addition to querySelector and querySelectorAll, the Element object supports:

  • getAttribute(name): Returns the value of the specified attribute. Returns an empty string if the attribute does not exist.

Your First Extension: Hello World

Let's create a simple extension that displays a list with a single item.

  1. Create a file named hello_world.js.
  2. Paste the following code:
var SYNURA = {
  name: "Hello World",
  api: 0,
  version: 0.1,
  description: "My first Synura extension.",
  license: "Apache-2.0",
  main: {
    home: function() {
      synura.open('/views/list', {
        styles: {
          title: "Hello World Extension"
        },
        models: {
          contents: [{
            title: "Welcome to Synura!",
            author: "Me",
            date: new Date().toLocaleDateString()
          }]
        }
      });
    }
  }
};
  1. Save the file.
  2. Load the extension into Synura (refer to the app's import instructions).
  3. Open the extension from the main menu. You should see a list with "Welcome to Synura!".

View Events

Extensions can listen to events from the UI using the onViewEvent callback.

Lifecycle Events (Automatic)

These events are triggered automatically by the system:

  • LOAD: Triggered when a view is first rendered or comes into focus. Use this to fetch data or initialize the view.

    Note: LOAD events do not reset the "Total Time" (t) metric in the Developer Overlay, ensuring that the initial user action (e.g., clicking a list item) remains the start time for performance measurement.

  • CLOSE: Triggered when a view is popped from the navigation stack.
  • REFRESH: Triggered by pull-to-refresh actions.

Interaction Events (User)

These events are triggered by user actions:

  • CLICK: Tapped on an item (list item, card, etc.).
  • DOUBLE_CLICK: Double-tapped an item.
  • SUBMIT: Form submitted.
  • QUERY: Search query entered.
  • MENU_CLICK: Top-level view menu item selected.
  • ITEM_MENU_CLICK: Item context menu action selected (e.g., on a comment or card).

Developer Overlay

When Developer Mode is enabled in Settings, a floating overlay displays real-time performance metrics for extension execution.

Metrics

Key Description
t Total time – End-to-end latency from user action to visible UI (includes Flutter render)
b Backend time – Total backend execution time, including extension logic (total minus fetch)
f Fetch time – Total HTTP request duration
c Fetch count – Number of HTTP requests made

Log Prefixes

  • c:/views/... – CreateView (new view opened)
  • u:/views/... – UpdateView (existing view refreshed)

Example Log

18:30:45 c:/views/list: t=1,245, b=89, f=1,120, c=2

This means:

  • Total end-to-end time: 1,245ms
  • Backend execution: 89ms
  • Network fetches: 1,120ms (2 requests)

AI Metrics

When the AI engine is enabled, additional metrics are logged for analysis and translation operations:

Key Description
t Total AI processing time
f Fetch time (LLM API call)
it Input tokens sent to the LLM
ot Output tokens received from the LLM
ap AI provider (e.g., gemini, openai)
am AI model name

AI Log Prefixes

  • a:/views/... – AI Analysis (content summarization, sentiment)
  • t:/views/... – AI Translation

Example AI Log

18:30:47 a:/views/post: t=890, f=850, it=1,024, ot=256, ap=gemini

Next Steps

  • Explore the API Reference to learn about other view types like post, chat, and settings.
  • Check out Examples for more complex use cases.