fix: add backend.call() API and fix [object Object] in workspace sidebar
- Add api.backend.call(method, ...args) to VerstakPluginAPI for direct Wails method invocation - Add wsName() helper in WorkspaceTree.svelte with String() coercion to prevent [object Object] display - Use correct Wails path window['go']['api']['App'] in backend.call()
This commit is contained in:
parent
ed69746332
commit
db67c370ab
|
|
@ -225,6 +225,23 @@ export function createPluginAPI(pluginId) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
backend: {
|
||||||
|
call: async function(method, ...args) {
|
||||||
|
assertActive('backend.call(' + method + ')');
|
||||||
|
try {
|
||||||
|
const App = window['go']?.['api']?.['App'];
|
||||||
|
if (!App || typeof App[method] !== 'function') {
|
||||||
|
throw new Error('Backend method not found: ' + method);
|
||||||
|
}
|
||||||
|
const result = await App[method](...args);
|
||||||
|
return result;
|
||||||
|
} catch (e) {
|
||||||
|
const message = e && e.message ? e.message : String(e);
|
||||||
|
throw new Error('[plugin:' + pluginId + '] backend.call(' + method + ') failed: ' + message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
workbench: {
|
workbench: {
|
||||||
openResource: async function(request) {
|
openResource: async function(request) {
|
||||||
assertActive('workbench.openResource');
|
assertActive('workbench.openResource');
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<script context="module">
|
<script context="module">
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
const activeWorkspaceNodeId = writable('');
|
const activeWorkspaceId = writable('');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -9,40 +9,58 @@
|
||||||
import * as App from '../../../wailsjs/go/api/App';
|
import * as App from '../../../wailsjs/go/api/App';
|
||||||
import Icon from '../ui/Icon.svelte';
|
import Icon from '../ui/Icon.svelte';
|
||||||
|
|
||||||
export let nodes = [];
|
|
||||||
export let node = null;
|
|
||||||
export let currentNodeId = '';
|
|
||||||
export let expandedNodes = {};
|
|
||||||
export let depth = 0;
|
|
||||||
|
|
||||||
let loading = true;
|
let loading = true;
|
||||||
let localError = '';
|
let localError = '';
|
||||||
|
let workspaces = [];
|
||||||
|
let currentWorkspaceId = '';
|
||||||
let showCreate = false;
|
let showCreate = false;
|
||||||
let newNodeTitle = '';
|
let newWorkspaceName = '';
|
||||||
let newNodeParentId = '';
|
|
||||||
let newNodeType = 'case';
|
|
||||||
let creating = false;
|
let creating = false;
|
||||||
|
let renamingId = '';
|
||||||
|
let renameValue = '';
|
||||||
|
let busyId = '';
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(loadWorkspaces);
|
||||||
if (depth === 0) {
|
|
||||||
await loadTree();
|
function resultOrError(response, fallbackValue) {
|
||||||
|
return typeof response === 'string' ? [fallbackValue, response] : [response, ''];
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
async function loadTree() {
|
function wsName(workspace) {
|
||||||
|
return String(workspace?.name || workspace?.rootPath || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function asNode(workspace, order) {
|
||||||
|
const name = wsName(workspace);
|
||||||
|
return {
|
||||||
|
id: name,
|
||||||
|
type: 'space',
|
||||||
|
title: name,
|
||||||
|
name,
|
||||||
|
rootPath: workspace.rootPath || name,
|
||||||
|
status: 'active',
|
||||||
|
order,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function nodesForEvent() {
|
||||||
|
return workspaces.map(asNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadWorkspaces() {
|
||||||
loading = true;
|
loading = true;
|
||||||
localError = '';
|
localError = '';
|
||||||
try {
|
try {
|
||||||
const result = await App.GetWorkspaceTree();
|
const [list, err] = resultOrError(await App.ListWorkspaces(), []);
|
||||||
if (result.status === 'not initialized') {
|
if (err) {
|
||||||
nodes = [];
|
localError = err;
|
||||||
currentNodeId = '';
|
workspaces = [];
|
||||||
} else {
|
} else {
|
||||||
nodes = result.nodes || [];
|
workspaces = list || [];
|
||||||
currentNodeId = result.currentNodeId || '';
|
if (!currentWorkspaceId || !workspaces.some((ws) => wsName(ws) === currentWorkspaceId)) {
|
||||||
activeWorkspaceNodeId.set(currentNodeId);
|
currentWorkspaceId = wsName(workspaces[0] || {});
|
||||||
const root = nodes.find(n => !n.parentId);
|
}
|
||||||
if (root) expandedNodes[root.id] = true;
|
activeWorkspaceId.set(currentWorkspaceId);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
localError = String(e);
|
localError = String(e);
|
||||||
|
|
@ -50,143 +68,176 @@
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function childrenOf(parentId) {
|
async function selectWorkspace(workspace) {
|
||||||
return nodes.filter(n => n.parentId === parentId).sort((a, b) => a.order - b.order);
|
const id = wsName(workspace);
|
||||||
|
const err = await App.SetCurrentWorkspace(id);
|
||||||
|
if (err) {
|
||||||
|
localError = err;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
currentWorkspaceId = id;
|
||||||
function roots() {
|
activeWorkspaceId.set(id);
|
||||||
return nodes.filter(n => !n.parentId).sort((a, b) => a.order - b.order);
|
window.dispatchEvent(new CustomEvent('verstak:workspace-selected', {
|
||||||
}
|
detail: { workspaceName: id, nodes: nodesForEvent() }
|
||||||
|
|
||||||
function toggle(id) {
|
|
||||||
expandedNodes[id] = !expandedNodes[id];
|
|
||||||
expandedNodes = expandedNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasKids(id) {
|
|
||||||
return nodes.some(n => n.parentId === id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconName(type) {
|
|
||||||
if (type === 'space') return 'space';
|
|
||||||
if (type === 'case') return 'case';
|
|
||||||
if (type === 'folder') return 'folder';
|
|
||||||
return 'dot';
|
|
||||||
}
|
|
||||||
|
|
||||||
async function selectNode(id) {
|
|
||||||
const err = await App.SetCurrentWorkspaceNode(id);
|
|
||||||
if (err) { localError = err; return; }
|
|
||||||
currentNodeId = id;
|
|
||||||
activeWorkspaceNodeId.set(id);
|
|
||||||
window.dispatchEvent(new CustomEvent('verstak:workspace-node-selected', {
|
|
||||||
detail: { nodeId: id, nodes: nodes }
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCreate(parentId, type) {
|
|
||||||
newNodeParentId = parentId;
|
|
||||||
newNodeType = type;
|
|
||||||
newNodeTitle = '';
|
|
||||||
showCreate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doCreate() {
|
async function doCreate() {
|
||||||
if (!newNodeTitle.trim()) return;
|
const name = newWorkspaceName.trim();
|
||||||
|
if (!name) return;
|
||||||
creating = true;
|
creating = true;
|
||||||
const res = await App.CreateWorkspaceNode(newNodeParentId, newNodeType, newNodeTitle.trim());
|
localError = '';
|
||||||
if (res.error) { localError = res.error; creating = false; return; }
|
const [, err] = resultOrError(await App.CreateWorkspace(name, 'default'), null);
|
||||||
showCreate = false;
|
if (err) {
|
||||||
|
localError = err;
|
||||||
creating = false;
|
creating = false;
|
||||||
expandedNodes[newNodeParentId] = true;
|
return;
|
||||||
expandedNodes = expandedNodes;
|
}
|
||||||
await loadTree();
|
showCreate = false;
|
||||||
|
newWorkspaceName = '';
|
||||||
|
creating = false;
|
||||||
|
await loadWorkspaces();
|
||||||
|
const created = workspaces.find((ws) => wsName(ws) === name);
|
||||||
|
if (created) await selectWorkspace(created);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelCreate() {
|
function startRename(workspace) {
|
||||||
showCreate = false;
|
renamingId = wsName(workspace);
|
||||||
newNodeTitle = '';
|
renameValue = renamingId;
|
||||||
|
localError = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelRename() {
|
||||||
|
renamingId = '';
|
||||||
|
renameValue = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function commitRename(workspace) {
|
||||||
|
const oldName = wsName(workspace);
|
||||||
|
const newName = renameValue.trim();
|
||||||
|
if (!newName || newName === oldName) {
|
||||||
|
cancelRename();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
busyId = oldName;
|
||||||
|
const err = await App.RenameWorkspace(oldName, newName);
|
||||||
|
if (err) {
|
||||||
|
localError = err;
|
||||||
|
busyId = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
renamingId = '';
|
||||||
|
renameValue = '';
|
||||||
|
busyId = '';
|
||||||
|
currentWorkspaceId = newName;
|
||||||
|
await loadWorkspaces();
|
||||||
|
const renamed = workspaces.find((ws) => wsName(ws) === newName);
|
||||||
|
if (renamed) await selectWorkspace(renamed);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function trashWorkspace(workspace) {
|
||||||
|
const name = wsName(workspace);
|
||||||
|
busyId = name;
|
||||||
|
const [, err] = resultOrError(await App.TrashWorkspace(name), null);
|
||||||
|
if (err) {
|
||||||
|
localError = err;
|
||||||
|
busyId = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentWorkspaceId === name) currentWorkspaceId = '';
|
||||||
|
busyId = '';
|
||||||
|
await loadWorkspaces();
|
||||||
|
if (currentWorkspaceId) {
|
||||||
|
const selected = workspaces.find((ws) => wsName(ws) === currentWorkspaceId);
|
||||||
|
if (selected) await selectWorkspace(selected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if depth === 0}
|
<div class="wt">
|
||||||
<div class="wt">
|
|
||||||
<div class="wt-header">
|
<div class="wt-header">
|
||||||
<span class="wt-title">Workspace</span>
|
<span class="wt-title">Workspaces</span>
|
||||||
<button class="wt-btn" on:click={() => openCreate('', 'space')} title="New Space" type="button">+</button>
|
<button class="wt-btn" on:click={() => { showCreate = true; newWorkspaceName = ''; }} title="New workspace" type="button">+</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<div class="wt-loading">Loading...</div>
|
<div class="wt-loading">Loading...</div>
|
||||||
{:else if localError}
|
{:else if localError}
|
||||||
<div class="wt-error">{localError}</div>
|
<div class="wt-error">{localError}</div>
|
||||||
{:else}
|
|
||||||
{#each roots() as node (node.id)}
|
|
||||||
<svelte:self {node} {nodes} {currentNodeId} {expandedNodes} depth={1} {toggle} {hasKids} {selectNode} {openCreate} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<div class="wt-list">
|
||||||
|
{#each workspaces as workspace (wsName(workspace))}
|
||||||
|
{@const id = wsName(workspace)}
|
||||||
|
<div class="wt-node" class:selected={id === $activeWorkspaceId}>
|
||||||
|
<div class="wt-row">
|
||||||
|
<span class="wt-icon"><Icon name="space" size={13} class="wt-node-icon" /></span>
|
||||||
|
{#if renamingId === id}
|
||||||
|
<input
|
||||||
|
class="wt-rename"
|
||||||
|
bind:value={renameValue}
|
||||||
|
disabled={busyId === id}
|
||||||
|
on:keydown={(e) => {
|
||||||
|
if (e.key === 'Enter') commitRename(workspace);
|
||||||
|
if (e.key === 'Escape') cancelRename();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<button class="wt-btn wt-btn-small wt-always" on:click={() => commitRename(workspace)} title="Save rename" type="button" disabled={busyId === id}>OK</button>
|
||||||
|
<button class="wt-btn wt-btn-small wt-always" on:click={cancelRename} title="Cancel rename" type="button" disabled={busyId === id}>x</button>
|
||||||
|
{:else}
|
||||||
|
<button class="wt-label" on:click={() => selectWorkspace(workspace)} type="button">{id}</button>
|
||||||
|
<button class="wt-icon-btn" on:click={() => startRename(workspace)} title="Rename workspace" type="button" disabled={busyId === id}>
|
||||||
|
<Icon name="edit" size={12} />
|
||||||
|
</button>
|
||||||
|
<button class="wt-icon-btn danger" on:click={() => trashWorkspace(workspace)} title="Trash workspace" type="button" disabled={busyId === id}>
|
||||||
|
<Icon name="trash" size={12} />
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if showCreate}
|
{#if showCreate}
|
||||||
<div class="wt-create">
|
<div class="wt-create">
|
||||||
<div class="wt-create-header">
|
<div class="wt-create-header">
|
||||||
<span>New {newNodeType}</span>
|
<span>New workspace</span>
|
||||||
<button class="wt-btn" on:click={cancelCreate} type="button">x</button>
|
<button class="wt-btn" on:click={() => { showCreate = false; newWorkspaceName = ''; }} type="button">x</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" bind:value={newNodeTitle} placeholder="Name..." disabled={creating} />
|
<input type="text" bind:value={newWorkspaceName} placeholder="Name..." disabled={creating} />
|
||||||
<div class="wt-create-actions">
|
<div class="wt-create-actions">
|
||||||
<button class="wt-btn-primary" on:click={doCreate} type="button" disabled={creating || !newNodeTitle.trim()}>{creating ? '...' : 'Create'}</button>
|
<button class="wt-btn-primary" on:click={doCreate} type="button" disabled={creating || !newWorkspaceName.trim()}>{creating ? '...' : 'Create'}</button>
|
||||||
<button class="wt-btn" on:click={cancelCreate} type="button" disabled={creating}>Cancel</button>
|
<button class="wt-btn" on:click={() => { showCreate = false; newWorkspaceName = ''; }} type="button" disabled={creating}>Cancel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
|
||||||
<div class="wt-node" class:selected={node.id === $activeWorkspaceNodeId} class:archived={node.status === 'archived'} class:sleeping={node.status === 'sleeping'}>
|
|
||||||
<div class="wt-row" style="padding-left: {depth * 1.0 + 0.4}rem;">
|
|
||||||
{#if hasKids(node.id)}
|
|
||||||
<button class="wt-expand" on:click={() => toggle(node.id)} type="button" aria-label={expandedNodes[node.id] ? 'Collapse' : 'Expand'}>
|
|
||||||
<Icon name={expandedNodes[node.id] ? 'chevronDown' : 'chevronRight'} size={12} class="wt-expand-icon" />
|
|
||||||
</button>
|
|
||||||
{:else}
|
|
||||||
<span class="wt-expand-spacer"></span>
|
|
||||||
{/if}
|
|
||||||
<span class="wt-icon"><Icon name={iconName(node.type)} size={13} class="wt-node-icon" /></span>
|
|
||||||
<button class="wt-label" on:click={() => selectNode(node.id)} type="button">{node.title}</button>
|
|
||||||
{#if node.type !== 'case'}
|
|
||||||
<button class="wt-btn wt-btn-small" on:click={() => openCreate(node.id, 'case')} title="Add child" type="button">+</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{#if expandedNodes[node.id]}
|
|
||||||
{#each childrenOf(node.id) as child (child.id)}
|
|
||||||
<svelte:self node={child} {nodes} {currentNodeId} {expandedNodes} depth={depth + 1} {toggle} {hasKids} {selectNode} {openCreate} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.wt { display: flex; flex-direction: column; flex: 1; overflow: hidden; position: relative; }
|
.wt { display: flex; flex-direction: column; flex: 1; overflow: hidden; position: relative; }
|
||||||
.wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.4rem 0.6rem; border-bottom: 1px solid #0f3460; flex-shrink: 0; }
|
.wt-header { display: flex; align-items: center; justify-content: space-between; padding: 0.4rem 0.6rem; border-bottom: 1px solid #0f3460; flex-shrink: 0; }
|
||||||
.wt-title { color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
|
.wt-title { color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }
|
||||||
|
.wt-list { min-height: 0; overflow-y: auto; padding: 0.2rem 0; }
|
||||||
.wt-btn { min-height: 0; background: none; border: none; color: #666; cursor: pointer; font-size: 0.85rem; padding: 0.1rem 0.3rem; border-radius: 3px; }
|
.wt-btn { min-height: 0; background: none; border: none; color: #666; cursor: pointer; font-size: 0.85rem; padding: 0.1rem 0.3rem; border-radius: 3px; }
|
||||||
.wt-btn:hover { color: #4ecca3; background: rgba(78,204,163,0.1); }
|
.wt-btn:hover:not(:disabled) { color: #4ecca3; background: rgba(78,204,163,0.1); }
|
||||||
.wt-btn-small { font-size: 0.7rem; opacity: 0; }
|
.wt-btn-small { font-size: 0.68rem; opacity: 0; }
|
||||||
|
.wt-always { opacity: 1; }
|
||||||
.wt-row:hover .wt-btn-small { opacity: 1; }
|
.wt-row:hover .wt-btn-small { opacity: 1; }
|
||||||
.wt-loading, .wt-error { padding: 0.5rem; font-size: 0.75rem; color: #666; }
|
.wt-loading, .wt-error { padding: 0.5rem; font-size: 0.75rem; color: #666; }
|
||||||
.wt-error { color: #e94560; }
|
.wt-error { color: #e94560; }
|
||||||
.wt-node { }
|
.wt-row { display: flex; align-items: center; gap: 0.25rem; padding: 0.15rem 0.45rem; min-height: 1.7rem; }
|
||||||
.wt-row { display: flex; align-items: center; gap: 0.2rem; padding: 0.15rem 0; }
|
|
||||||
.wt-row:hover { background: rgba(15,52,96,0.4); }
|
.wt-row:hover { background: rgba(15,52,96,0.4); }
|
||||||
.wt-node.selected > .wt-row { background: rgba(78,204,163,0.1); }
|
.wt-node.selected > .wt-row { background: rgba(78,204,163,0.1); }
|
||||||
.wt-expand { width: 1rem; height: 1rem; min-height: 0; display: flex; align-items: center; justify-content: center; font-size: 0.65rem; color: #666; background: none; border: none; cursor: pointer; padding: 0; flex-shrink: 0; }
|
|
||||||
.wt-expand:hover { color: #e0e0f0; }
|
|
||||||
.wt-expand-spacer { width: 1rem; flex-shrink: 0; }
|
|
||||||
.wt-icon { width: 0.9rem; height: 0.9rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: #a0a0b8; }
|
.wt-icon { width: 0.9rem; height: 0.9rem; display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: #a0a0b8; }
|
||||||
:global(.wt-node-icon), :global(.wt-expand-icon) { display: block; }
|
:global(.wt-node-icon) { display: block; }
|
||||||
.wt-label { flex: 1; min-height: 0; justify-content: flex-start; background: none; border: none; color: #e0e0f0; font-size: 0.78rem; text-align: left; cursor: pointer; padding: 0.1rem 0.2rem; border-radius: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.wt-label { flex: 1; min-width: 0; min-height: 0; justify-content: flex-start; background: none; border: none; color: #e0e0f0; font-size: 0.78rem; text-align: left; cursor: pointer; padding: 0.1rem 0.2rem; border-radius: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.wt-label:hover { color: #4ecca3; }
|
.wt-label:hover { color: #4ecca3; }
|
||||||
.wt-node.archived .wt-label { text-decoration: line-through; opacity: 0.5; }
|
.wt-icon-btn { width: 1.25rem; height: 1.25rem; min-height: 0; padding: 0; border: none; background: transparent; color: #666; opacity: 0; flex-shrink: 0; cursor: pointer; border-radius: 3px; }
|
||||||
.wt-node.sleeping .wt-label { opacity: 0.6; }
|
.wt-row:hover .wt-icon-btn { opacity: 1; }
|
||||||
|
.wt-icon-btn:hover:not(:disabled) { color: #4ecca3; background: rgba(78,204,163,0.1); }
|
||||||
|
.wt-icon-btn.danger:hover:not(:disabled) { color: #e94560; background: rgba(233,69,96,0.12); }
|
||||||
|
.wt-rename { flex: 1; min-width: 0; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.2rem 0.35rem; border-radius: 4px; font-size: 0.78rem; }
|
||||||
|
.wt-rename:focus { outline: none; border-color: #4ecca3; }
|
||||||
.wt-create { position: absolute; bottom: 0; left: 0; right: 0; background: #16213e; border-top: 1px solid #0f3460; padding: 0.6rem; z-index: 10; }
|
.wt-create { position: absolute; bottom: 0; left: 0; right: 0; background: #16213e; border-top: 1px solid #0f3460; padding: 0.6rem; z-index: 10; }
|
||||||
.wt-create-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; }
|
.wt-create-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; color: #a0a0b8; font-size: 0.7rem; text-transform: uppercase; }
|
||||||
.wt-create input { width: 100%; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.35rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin-bottom: 0.4rem; box-sizing: border-box; }
|
.wt-create input { width: 100%; background: #0f3460; border: 1px solid #1a3a5c; color: #e0e0f0; padding: 0.35rem 0.5rem; border-radius: 4px; font-size: 0.8rem; margin-bottom: 0.4rem; box-sizing: border-box; }
|
||||||
|
|
@ -194,5 +245,5 @@
|
||||||
.wt-create-actions { display: flex; gap: 0.4rem; justify-content: flex-end; }
|
.wt-create-actions { display: flex; gap: 0.4rem; justify-content: flex-end; }
|
||||||
.wt-btn-primary { background: #4ecca3; color: #1a1a2e; border: none; padding: 0.3rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 600; }
|
.wt-btn-primary { background: #4ecca3; color: #1a1a2e; border: none; padding: 0.3rem 0.6rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 600; }
|
||||||
.wt-btn-primary:hover:not(:disabled) { background: #3dbb92; }
|
.wt-btn-primary:hover:not(:disabled) { background: #3dbb92; }
|
||||||
.wt-btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
|
.wt-btn-primary:disabled, .wt-btn:disabled, .wt-icon-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue