Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/cross-promotion-measurement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Cross-promotion measurement

ScriptWidget's landing page accepts these attribution parameters:

- `utm_source`
- `utm_medium`
- `utm_campaign`
- `utm_content`

The page keeps the values for the current browser session and emits these events through
`window.dataLayer`, `window.plausible` (when present), and the `xnu:analytics` custom event:

- `landing_view`
- `app_store_click`
- `github_click`
- `tutorial_click`

The initial Remoboard placement uses:

```text
utm_source=remoboard
utm_medium=in_app
utm_campaign=cross_promo
utm_content=home_featured_card
```

Use `landing_view` as the number of visits from Remoboard and `app_store_click` as the
number of App Store handoffs. Report the click-through rate as App Store handoffs divided
by landing visits.

To attribute first-time downloads, create an App Store Connect campaign named
`remoboard_cross_promo`, then add the generated `pt`, `ct`, and `mt` parameters to the
App Store URL in `site/index.html`. Apple requires a provider token generated by App Store
Connect; it must not be guessed or created manually.
54 changes: 54 additions & 0 deletions site/analytics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
(function () {
"use strict";

var allowedParameters = ["utm_source", "utm_medium", "utm_campaign", "utm_content"];
var query = new URLSearchParams(window.location.search);
var attribution = {};

allowedParameters.forEach(function (name) {
var value = query.get(name);
if (value) {
attribution[name] = value.slice(0, 100);
}
});

if (Object.keys(attribution).length > 0) {
try {
window.sessionStorage.setItem("scriptwidget_attribution", JSON.stringify(attribution));
} catch (_) {
// Attribution remains available for this page view when storage is unavailable.
}
} else {
try {
attribution = JSON.parse(window.sessionStorage.getItem("scriptwidget_attribution")) || {};
} catch (_) {
attribution = {};
}
}

function track(name, properties) {
var eventProperties = Object.assign({}, attribution, properties || {});
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ event: name, properties: eventProperties });

if (typeof window.plausible === "function") {
window.plausible(name, { props: eventProperties });
}

window.dispatchEvent(new CustomEvent("xnu:analytics", {
detail: { name: name, properties: eventProperties }
}));
}

window.xnuTrack = track;
track("landing_view", { page: "scriptwidget" });

document.querySelectorAll("[data-track-event]").forEach(function (link) {
link.addEventListener("click", function () {
track(link.dataset.trackEvent, {
label: link.dataset.trackLabel || "",
destination: link.hostname
});
});
});
}());
4 changes: 2 additions & 2 deletions site/index.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>ScriptWidget — Native widgets with JavaScript + JSX</title><meta name="description" content="Create native iPhone, iPad, and Mac widgets with JavaScript, JSX, and ScriptWidget Studio."><link rel="canonical" href="https://xnu.app/scriptwidget/"><meta property="og:title" content="ScriptWidget"><meta property="og:description" content="Build native Apple-platform widgets with JavaScript and JSX."><meta property="og:type" content="website"><meta property="og:url" content="https://xnu.app/scriptwidget/"><meta property="og:image" content="https://raw.githubusercontent.com/everettjf/scriptwidget/main/Resource/WidgetDemoScreenshots/_contact-sheet-1.jpg"><link rel="stylesheet" href="styles.css"></head>
<body><header class="wrap nav"><a class="brand" href="./">ScriptWidget</a><nav class="navlinks" aria-label="Main navigation"><a class="optional" href="#docs">Docs</a><a href="https://github.com/everettjf/scriptwidget">GitHub</a><a href="https://discord.gg/eGzEaP6TzR">Discord</a></nav></header>
<main><section class="wrap hero"><div><div class="eyebrow">Open source · iOS · iPadOS · macOS</div><h1>Widgets, written your way.</h1><p class="lede">Write JavaScript and JSX, preview in ScriptWidget Studio, and render native WidgetKit experiences across Apple platforms—without authoring every widget in Swift.</p><div class="actions"><a class="button primary" href="https://apps.apple.com/us/app/scriptwidget/id1555600758">Get ScriptWidget ↗</a><a class="button" href="https://github.com/everettjf/scriptwidget">Star on GitHub ↗</a><a class="button" href="https://github.com/everettjf/scriptwidget/blob/main/docs/five-minute-tutorial.md">Start in five minutes</a></div></div><div class="visual"><img src="https://raw.githubusercontent.com/everettjf/scriptwidget/main/Resource/WidgetDemoScreenshots/_contact-sheet-1.jpg" alt="A gallery of widgets created with ScriptWidget"></div></section>
<main><section class="wrap hero"><div><div class="eyebrow">Open source · iOS · iPadOS · macOS</div><h1>Widgets, written your way.</h1><p class="lede">Write JavaScript and JSX, preview in ScriptWidget Studio, and render native WidgetKit experiences across Apple platforms—without authoring every widget in Swift.</p><div class="actions"><a class="button primary" data-track-event="app_store_click" data-track-label="hero_primary" href="https://apps.apple.com/us/app/scriptwidget/id1555600758">Get ScriptWidget ↗</a><a class="button" data-track-event="github_click" data-track-label="hero_secondary" href="https://github.com/everettjf/scriptwidget">Star on GitHub ↗</a><a class="button" data-track-event="tutorial_click" data-track-label="hero_secondary" href="https://github.com/everettjf/scriptwidget/blob/main/docs/five-minute-tutorial.md">Start in five minutes</a></div></div><div class="visual"><img src="https://raw.githubusercontent.com/everettjf/scriptwidget/main/Resource/WidgetDemoScreenshots/_contact-sheet-1.jpg" alt="A gallery of widgets created with ScriptWidget"></div></section>
<div class="strip"><div class="wrap"><span>JavaScriptCore runtime</span><span>Native SwiftUI rendering</span><span>Live preview on Mac</span><span>Community gallery</span></div></div>
<section class="section"><div class="wrap"><div class="section-label">Why ScriptWidget</div><h2>From an idea to a native widget.</h2><div class="grid"><article class="card"><h3>Write in JavaScript + JSX</h3><p>Compose familiar declarative layouts and use versioned APIs for data, files, networking, storage, device state, and more.</p></article><article class="card"><h3>Build in Studio</h3><p>Edit with diagnostics, inspect runtime output, and preview multiple widget families without repeating an Xcode build loop.</p></article><article class="card"><h3>Share real packages</h3><p>Use versioned manifests, explicit permissions, host allowlists, and the community Gallery to distribute reusable widgets.</p></article></div></div></section>
<section class="section"><div class="wrap"><div class="section-label">A small example</div><h2>JSX in. Native WidgetKit out.</h2><pre class="code"><code><span class="purple">$render</span>(&#10; &lt;vstack frame=<span class="pink">"max"</span> padding=<span class="pink">"16"</span> background=<span class="pink">"#171923"</span>&gt;&#10; &lt;text font=<span class="pink">"caption"</span> color=<span class="pink">"#a9b0c0"</span>&gt;Today&lt;/text&gt;&#10; &lt;text font=<span class="pink">"largeTitle"</span> color=<span class="pink">"#ffffff"</span>&gt;Build something useful.&lt;/text&gt;&#10; &lt;/vstack&gt;&#10;);</code></pre></div></section>
<section class="section" id="docs"><div class="wrap"><div class="section-label">Documentation</div><h2>Explore the platform.</h2><div class="docs"><a class="doc" href="https://github.com/everettjf/scriptwidget/blob/main/docs/runtime-api.md"><strong>Runtime API →</strong><span>Rendering, storage, network, system, device, and package APIs.</span></a><a class="doc" href="https://github.com/everettjf/scriptwidget/tree/main/Shared/ScriptWidgetRuntime/Widget/Component"><strong>JSX components →</strong><span>The native components and modifiers available to scripts.</span></a><a class="doc" href="https://github.com/everettjf/scriptwidget/blob/main/docs/gallery.md"><strong>Gallery →</strong><span>Discover, verify, install, and publish community packages.</span></a></div></div></section></main>
<footer class="footer"><div class="wrap"><span>Built in the open by Everett.</span><span><a href="privacy/">Privacy</a><a href="https://xnu.app/">All projects</a><a href="mailto:xnuapp@gmail.com">Support</a></span></div></footer></body></html>
<footer class="footer"><div class="wrap"><span>Built in the open by Everett.</span><span><a href="privacy/">Privacy</a><a href="https://xnu.app/">All projects</a><a href="mailto:xnuapp@gmail.com">Support</a></span></div></footer><script src="analytics.js" defer></script></body></html>