mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-08-03 21:12:24 +00:00
feat(automationPlugin): improve button styles and add copyTo option for plugin output
This commit is contained in:
parent
0561f826a2
commit
8460d894e1
8 changed files with 108 additions and 57 deletions
|
|
@ -6,6 +6,7 @@
|
|||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"watch": "vite build --watch",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest",
|
||||
"lint": "eslint .",
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue