feat(automationPlugin): improve button styles and add copyTo option for plugin output

This commit is contained in:
Johannes Millan 2025-11-27 16:24:30 +01:00
parent 0561f826a2
commit 8460d894e1
8 changed files with 108 additions and 57 deletions

View file

@ -6,6 +6,7 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"watch": "vite build --watch",
"preview": "vite preview",
"test": "vitest",
"lint": "eslint .",

View file

@ -13,6 +13,9 @@
--pico-primary-focus: var(--c-accent);
--pico-card-background-color: var(--card-bg);
--pico-border-color: var(--divider-color);
/* Tighten default control padding to avoid oversized buttons */
--pico-form-element-spacing-vertical: 0.35rem;
--pico-form-element-spacing-horizontal: 0.75rem;
/* Additional Pico overrides for better integration */
--pico-form-element-background-color: var(--bg-darker);
@ -20,6 +23,46 @@
--pico-form-element-color: var(--text-color);
}
/* Force primary button color */
button:not(.outline):not(.secondary):not(.contrast):not(.close-btn) {
background-color: var(--c-primary);
border-color: var(--c-primary);
color: #fff;
}
/* Primary outline buttons */
button.outline:not(.secondary):not(.contrast),
button.outline.secondary {
background-color: transparent !important;
color: var(--c-primary) !important;
border-color: var(--c-primary) !important;
}
button.outline:not(.secondary):not(.contrast):hover,
button.outline.secondary:hover {
background-color: var(--c-primary) !important;
color: #fff !important;
}
/* Make secondary outlines look like primary outlines for cancel buttons */
/* Force warn color for delete buttons (using contrast class) */
button.contrast {
background-color: var(--c-warn);
border-color: var(--c-warn);
color: #fff;
}
button.outline.contrast {
background-color: transparent !important;
border-color: var(--c-warn) !important;
color: var(--c-warn) !important;
}
/* Checkboxes */
input[type='checkbox']:checked {
background-color: var(--c-primary);
border-color: var(--c-primary);
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
}
body {
margin: 0;
padding: 0;
@ -37,15 +80,14 @@ main.container {
max-width: 960px !important;
}
/* Small button utility */
button.btn-sm,
a[role='button'].btn-sm,
input[type='submit'].btn-sm,
input[type='button'].btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
width: auto;
margin-bottom: 0;
/* Reduce button height globally */
button,
[role='button'],
input[type='submit'],
input[type='button'],
input[type='reset'] {
min-height: auto;
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
}
/* Dialog styles */
@ -60,26 +102,6 @@ dialog article header {
align-items: center;
}
/* Icon-only close button */
button.close-btn {
background: transparent;
border: none;
padding: 0;
margin: 0;
width: auto;
color: var(--text-color);
opacity: 0.7;
cursor: pointer;
font-size: 1.5rem;
line-height: 1;
}
button.close-btn:hover {
background: transparent;
opacity: 1;
color: var(--primary-color);
}
/* Table styles */
table {
width: 100%;

View file

@ -31,7 +31,7 @@ export function ActionDialog(props: ActionDialogProps) {
title={props.initialAction ? 'Edit Action' : 'Add Action'}
footer={
<div class="grid">
<button class="secondary outline" onClick={props.onClose}>
<button class="outline secondary" onClick={props.onClose}>
Cancel
</button>
<button onClick={() => props.onSave(action())}>Save</button>

View file

@ -33,7 +33,7 @@ export function ConditionDialog(props: ConditionDialogProps) {
title={props.initialCondition ? 'Edit Condition' : 'Add Condition'}
footer={
<div class="grid">
<button class="secondary outline" onClick={props.onClose}>
<button class="outline secondary" onClick={props.onClose}>
Cancel
</button>
<button onClick={() => props.onSave(condition())}>Save</button>

View file

@ -111,7 +111,7 @@ export function RuleEditor(props: RuleEditorProps) {
</div>
<div style={{ 'text-align': 'right' }}>
<button
class="secondary outline"
class="outline secondary"
onClick={props.onCancel}
style={{ 'margin-right': '0.5rem' }}
>
@ -159,7 +159,6 @@ export function RuleEditor(props: RuleEditorProps) {
</div>
<div style={{ 'text-align': 'right' }}>
<button
class="btn-sm outline"
onClick={() => {
setEditingConditionIndex(-1);
setIsConditionDialogOpen(true);
@ -189,7 +188,7 @@ export function RuleEditor(props: RuleEditorProps) {
</td>
<td style={{ 'text-align': 'right' }}>
<button
class="btn-sm outline secondary"
class="outline"
onClick={() => {
setEditingConditionIndex(i());
setIsConditionDialogOpen(true);
@ -198,10 +197,7 @@ export function RuleEditor(props: RuleEditorProps) {
>
Edit
</button>
<button
class="btn-sm outline contrast"
onClick={() => handleRemoveCondition(i())}
>
<button class="outline contrast" onClick={() => handleRemoveCondition(i())}>
</button>
</td>
@ -221,7 +217,6 @@ export function RuleEditor(props: RuleEditorProps) {
</div>
<div style={{ 'text-align': 'right' }}>
<button
class="btn-sm outline"
onClick={() => {
setEditingActionIndex(-1);
setIsActionDialogOpen(true);
@ -251,7 +246,7 @@ export function RuleEditor(props: RuleEditorProps) {
</td>
<td style={{ 'text-align': 'right' }}>
<button
class="btn-sm outline secondary"
class="outline"
onClick={() => {
setEditingActionIndex(i());
setIsActionDialogOpen(true);
@ -260,10 +255,7 @@ export function RuleEditor(props: RuleEditorProps) {
>
Edit
</button>
<button
class="btn-sm outline contrast"
onClick={() => handleRemoveAction(i())}
>
<button class="outline contrast" onClick={() => handleRemoveAction(i())}>
</button>
</td>

View file

@ -17,7 +17,7 @@ export function RuleList(props: RuleListProps) {
<h2>Automation Rules</h2>
</div>
<div style={{ 'text-align': 'right' }}>
<button class="btn-sm" onClick={props.onCreate}>
<button class="outline" onClick={props.onCreate}>
+ New Rule
</button>
</div>
@ -60,13 +60,13 @@ export function RuleList(props: RuleListProps) {
</td>
<td style={{ 'text-align': 'right' }}>
<button
class="outline secondary btn-sm"
class="outline"
onClick={() => props.onEdit(rule)}
style={{ 'margin-right': '0.5rem' }}
>
Edit
</button>
<button class="outline contrast btn-sm" onClick={() => props.onDelete(rule)}>
<button class="outline contrast" onClick={() => props.onDelete(rule)}>
Delete
</button>
</td>

View file

@ -4,7 +4,12 @@ import solidPlugin from 'vite-plugin-solid';
import { superProductivityPlugin } from '@super-productivity/vite-plugin';
export default defineConfig({
plugins: [solidPlugin(), superProductivityPlugin()],
plugins: [
solidPlugin(),
superProductivityPlugin({
copyTo: '../../../src/assets/bundled-plugins/automation-plugin',
}),
],
test: {
environment: 'jsdom',
globals: true,

View file

@ -1,4 +1,4 @@
import { Plugin, UserConfig } from 'vite';
import { Plugin } from 'vite';
import path from 'path';
import fs from 'fs';
@ -9,18 +9,21 @@ export interface SuperProductivityPluginOptions {
* Default: true
*/
inlineAssets?: boolean;
/**
* Directory to copy the build output to.
* Useful for watching and auto-updating the plugin in the main app.
*/
copyTo?: string;
}
export function superProductivityPlugin(
export const superProductivityPlugin = (
options: SuperProductivityPluginOptions = {},
): Plugin {
const { inlineAssets = true } = options;
let config: UserConfig;
): Plugin => {
const { inlineAssets = true, copyTo } = options;
return {
name: 'super-productivity-plugin',
config(userConfig) {
config = userConfig;
config: () => {
return {
build: {
outDir: 'dist',
@ -46,7 +49,7 @@ export function superProductivityPlugin(
},
closeBundle: {
order: 'post',
handler() {
handler: () => {
const distDir = path.resolve(process.cwd(), 'dist');
// 1. Copy manifest.json
@ -115,7 +118,35 @@ export function superProductivityPlugin(
fs.writeFileSync(htmlPath, html);
console.log('✅ Assets inlined successfully');
}
// 4. Copy to destination if specified
if (copyTo) {
const destDir = path.resolve(process.cwd(), copyTo);
if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true });
}
// Recursive copy function
const copyRecursive = (src: string, dest: string): void => {
const entries = fs.readdirSync(src, { withFileTypes: true });
if (!fs.existsSync(dest)) fs.mkdirSync(dest);
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
copyRecursive(srcPath, destPath);
} else {
fs.copyFileSync(srcPath, destPath);
}
}
};
copyRecursive(distDir, destDir);
console.log(`✅ Copied build to ${copyTo}`);
}
},
},
};
}
};