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
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.1.2] - 2026-03-04

### Added

- `isNotionEnabled` toggle in the interactive services prompt (Announcements & Onboarding)
- `isNotionEnabled` flag post-processed in `global.config.ts` alongside the other service flags
- `unoff create` now shows a clear "coming soon" message and exits cleanly for `penpot-plugin`, `sketch-plugin`, and `framer-plugin` instead of attempting scaffold
- `unoff help` now separates available platforms from coming soon platforms
- Service list (Supabase, Mixpanel, Sentry, Notion) displayed in `unoff help` under `create`

### Changed

- `.env.local` is now always fully generated with all vars regardless of selected services — only the flags in `global.config.ts` are toggled
- `.env.sentry-build-plugin` is now always generated unconditionally
- README and help output updated to reflect platform availability status

## [0.1.1] - 2026-03-04

### Added
Expand Down Expand Up @@ -43,5 +59,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- 🚧 Sketch plugin template (coming soon)
- 🚧 Framer plugin template (coming soon)

[0.1.2]: https://github.com/yelbolt/unoff-cli/compare/v0.1.1...v0.1.2
[0.1.1]: https://github.com/yelbolt/unoff-cli/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/yelbolt/unoff-cli/releases/tag/v0.1.0
23 changes: 21 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,25 @@ Show all commands, available platforms, and workers.

### `unoff create <platform>`

Scaffold a new plugin project. Platforms: `figma-plugin`, `penpot-plugin`, `sketch-plugin`, `framer-plugin`.
Scaffold a new plugin project.

| Platform | Status |
| -------- | ------ |
| `figma-plugin` | ✅ Available |
| `penpot-plugin` | 🚧 Coming soon |
| `sketch-plugin` | 🚧 Coming soon |
| `framer-plugin` | 🚧 Coming soon |

The interactive prompt will ask for plugin name, output directory, GitHub username, author, license, and which external services to enable:

| Service | Default |
| ------- | ------- |
| Supabase (Database & Authentication) | ✅ |
| Mixpanel (Analytics) | ✅ |
| Sentry (Error Monitoring) | ✅ |
| Notion (Announcements & Onboarding) | ✅ |

Selected services update the `is*Enabled` flags in `global.config.ts`. All environment variable placeholders are always generated in `.env.local`.

```bash
unoff create figma-plugin
Expand Down Expand Up @@ -96,9 +114,10 @@ unoff remove announcement-worker
## Features

- 🚀 Quick setup with interactive CLI
- 📦 Multiple platform support (Figma, Penpot, Sketch, Framer)
- 📦 Multiple platform support (Figma, Penpot 🚧, Sketch 🚧, Framer 🚧)
- 🔧 Built-in development server with hot reload
- 🏗️ Production-ready build system
- 🎛️ Toggleable external services (Supabase, Mixpanel, Sentry, Notion)
- 🔐 Authentication scaffolding (Supabase)
- 💳 License management (LemonSqueezy)
- 📊 Analytics and monitoring (Mixpanel, Sentry)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@unoff/cli",
"version": "0.1.1",
"version": "0.1.2",
"description": "A CLI tool to create plugins for Figma, Penpot, Sketch, and Framer",
"main": "dist/index.js",
"type": "module",
Expand Down Expand Up @@ -54,4 +54,4 @@
"type": "git",
"url": "https://github.com/yelbolt/unoff-cli.git"
}
}
}
23 changes: 14 additions & 9 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,26 @@ program
.command('create <platform>')
.description('Create a new plugin for a specific platform')
.action(async (platform: string) => {
const validPlatforms = [
'figma-plugin',
'penpot-plugin',
'sketch-plugin',
'framer-plugin',
]

if (!validPlatforms.includes(platform)) {
const availablePlatforms = ['figma-plugin']
const comingSoonPlatforms = ['penpot-plugin', 'sketch-plugin', 'framer-plugin']
const allPlatforms = [...availablePlatforms, ...comingSoonPlatforms]

if (!allPlatforms.includes(platform)) {
console.error(chalk.red(`\n❌ Invalid platform: ${platform}`))
console.error(
chalk.yellow(`\nValid platforms: ${validPlatforms.join(', ')}\n`)
chalk.yellow(`\nAvailable platforms: ${availablePlatforms.join(', ')}`)
)
console.error(
chalk.gray(`Coming soon: ${comingSoonPlatforms.join(', ')}\n`)
)
process.exit(1)
}

if (comingSoonPlatforms.includes(platform)) {
console.error(chalk.yellow(`\n🚧 ${platform} template is coming soon!\n`))
process.exit(0)
}

try {
await createPlugin(platform)
} catch (error) {
Expand Down
49 changes: 20 additions & 29 deletions src/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ interface TemplateData {
isSupabaseEnabled: boolean
isMixpanelEnabled: boolean
isSentryEnabled: boolean
isNotionEnabled: boolean
}

function slugify(text: string): string {
Expand Down Expand Up @@ -133,6 +134,11 @@ export async function createPlugin(platform: string) {
value: 'sentry',
checked: true,
},
{
name: 'Notion (Announcements & Onboarding)',
value: 'notion',
checked: true,
},
],
},
])
Expand Down Expand Up @@ -206,6 +212,7 @@ export async function createPlugin(platform: string) {
isSupabaseEnabled: selectedServices.includes('supabase'),
isMixpanelEnabled: selectedServices.includes('mixpanel'),
isSentryEnabled: selectedServices.includes('sentry'),
isNotionEnabled: selectedServices.includes('notion'),
}

// Copy template directory excluding node_modules and dist
Expand Down Expand Up @@ -248,6 +255,10 @@ export async function createPlugin(platform: string) {
/isSentryEnabled: true/,
`isSentryEnabled: ${templateData.isSentryEnabled}`
)
.replace(
/isNotionEnabled: true/,
`isNotionEnabled: ${templateData.isNotionEnabled}`
)
await fs.writeFile(globalConfigPath, configContent, 'utf-8')
}

Expand Down Expand Up @@ -362,53 +373,35 @@ async function generateEnvFiles(outputDir: string, templateData: TemplateData) {
const envSentryPath = path.join(outputDir, '.env.sentry-build-plugin')

// Generate .env.local content
let envLocalContent = ''

if (templateData.isSupabaseEnabled) {
envLocalContent += `
const envLocalContent = `
# Supabase Configuration
# Get your keys from: https://supabase.com/dashboard/project/_/settings/api
VITE_SUPABASE_URL='YOUR_SUPABASE_URL'
VITE_SUPABASE_PUBLIC_ANON_KEY='YOUR_SUPABASE_ANON_KEY'

`
}

if (templateData.isSentryEnabled) {
envLocalContent += `
# Sentry Configuration
# Get your DSN from: https://sentry.io/settings/projects/
VITE_SENTRY_DSN='YOUR_SENTRY_DSN'

`
}

if (templateData.isMixpanelEnabled) {
envLocalContent += `
# Mixpanel Configuration
# Get your token from: https://mixpanel.com/settings/project/
VITE_MIXPANEL_TOKEN='YOUR_MIXPANEL_TOKEN'

`
}

// Add common configuration
envLocalContent += `
# Authentication & Workers
VITE_AUTH_URL='https://auth.${templateData.pluginSlug}.com'

# Workers URLs
VITE_AUTH_WORKER_URL='https://oauth.yelbolt.workers.dev'
VITE_CORS_WORKER_URL='https://cors.yelbolt.workers.dev'
VITE_ANNOUNCEMENTS_WORKER_URL='https://68e83449-announcements.yelbolt.workers.dev/'

# Notion — Content Management (Announcements & Onboarding)
# Duplicate the databases to your Notion workspace and paste their IDs below
# Create an internal integration at https://www.notion.so/my-integrations and add it to both databases
VITE_NOTION_ANNOUNCEMENTS_ID='YOUR_ANNOUNCEMENTS_DB_ID'
VITE_NOTION_ONBOARDING_ID='YOUR_ONBOARDING_DB_ID'
VITE_NOTION_API_KEY='YOUR_NOTION_API_KEY'

# Workers URLs
VITE_AUTH_WORKER_URL='https://oauth.yelbolt.workers.dev'
VITE_ANNOUNCEMENTS_WORKER_URL='https://68e83449-announcements.yelbolt.workers.dev/'
VITE_CORS_WORKER_URL='https://cors.yelbolt.workers.dev'

# LemonSqueezy (if using payments)
# You can test with this test license key: 0DE7C002-1F28-49E8-A444-B424F346416E
VITE_LEMONSQUEEZY_URL='https://api.lemonsqueezy.com/v1'
Expand All @@ -421,12 +414,10 @@ VITE_TOLGEE_API_KEY='YOUR_TOLGEE_API_KEY'

await fs.writeFile(envLocalPath, envLocalContent, 'utf-8')

// Generate .env.sentry-build-plugin only if Sentry is enabled
if (templateData.isSentryEnabled) {
const envSentryContent = `# Sentry Build Plugin Configuration
// Generate .env.sentry-build-plugin
const envSentryContent = `# Sentry Build Plugin Configuration
# Generate your auth token from: https://sentry.io/settings/account/api/auth-tokens/
SENTRY_AUTH_TOKEN='YOUR_SENTRY_AUTH_TOKEN'
`
await fs.writeFile(envSentryPath, envSentryContent, 'utf-8')
}
await fs.writeFile(envSentryPath, envSentryContent, 'utf-8')
}
27 changes: 25 additions & 2 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
import chalk from 'chalk'
import { WORKERS, WORKER_SCRIPTS } from './add.js'

const PLATFORMS = ['figma-plugin', 'penpot-plugin', 'sketch-plugin', 'framer-plugin']
const AVAILABLE_PLATFORMS = ['figma-plugin']
const COMING_SOON_PLATFORMS = [
'penpot-plugin',
'sketch-plugin',
'framer-plugin',
]

const SERVICES = [
{ name: 'supabase', label: 'Supabase (Database & Authentication)' },
{ name: 'mixpanel', label: 'Mixpanel (Analytics)' },
{ name: 'sentry', label: 'Sentry (Error Monitoring)' },
{ name: 'notion', label: 'Notion (Announcements & Onboarding)' },
]

function row(cmd: string, desc: string) {
return ` ${chalk.cyan(cmd.padEnd(36))}${chalk.white(desc)}`
Expand Down Expand Up @@ -32,7 +44,18 @@ export function showHelp() {
'Scaffold a new plugin project'
)
)
console.log(chalk.gray(` ${''.padEnd(36)}Platforms: ${PLATFORMS.join(', ')}`))
console.log(
chalk.gray(` ${''.padEnd(36)}Available: ${AVAILABLE_PLATFORMS.join(', ')}`)
)
console.log(
chalk.gray(
` ${''.padEnd(36)}Coming soon: ${COMING_SOON_PLATFORMS.join(', ')}`
)
)
console.log(chalk.gray(` ${''.padEnd(36)}Services (toggleable, all enabled by default):`))
for (const s of SERVICES) {
console.log(chalk.gray(` ${''.padEnd(38)}· ${s.label}`))
}
console.log()

console.log(chalk.dim(' — Development'))
Expand Down
Loading