@worca/ui 0.46.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/main.bundle.js +1556 -1429
- package/app/main.bundle.js.map +4 -4
- package/app/styles.css +90 -0
- package/package.json +1 -1
- package/server/templates-routes.js +70 -0
package/app/styles.css
CHANGED
|
@@ -2371,6 +2371,16 @@ sl-input [slot="prefix"] {
|
|
|
2371
2371
|
margin-top: 2px;
|
|
2372
2372
|
}
|
|
2373
2373
|
|
|
2374
|
+
.settings-field-hint--warn {
|
|
2375
|
+
color: var(--warning, #b8860b);
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
.settings-field-hint--warn code {
|
|
2379
|
+
color: inherit;
|
|
2380
|
+
background: transparent;
|
|
2381
|
+
padding: 0;
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2374
2384
|
/* Export-mode picker (standalone vs delta): roomy rows, with each option's
|
|
2375
2385
|
description on its own line aligned under the option title (not the radio). */
|
|
2376
2386
|
.export-mode-group sl-radio::part(base) {
|
|
@@ -3637,6 +3647,86 @@ sl-option.template-grouped:focus::part(suffix) {
|
|
|
3637
3647
|
line-height: 1.5;
|
|
3638
3648
|
}
|
|
3639
3649
|
|
|
3650
|
+
/* Pipeline-template header row: label on the left, Suggest button on the right. */
|
|
3651
|
+
.template-select-header {
|
|
3652
|
+
display: flex;
|
|
3653
|
+
align-items: center;
|
|
3654
|
+
justify-content: space-between;
|
|
3655
|
+
gap: 8px;
|
|
3656
|
+
margin-bottom: 4px;
|
|
3657
|
+
}
|
|
3658
|
+
|
|
3659
|
+
.template-select-header .settings-label {
|
|
3660
|
+
margin-bottom: 0;
|
|
3661
|
+
}
|
|
3662
|
+
|
|
3663
|
+
.btn-suggest-template::part(base) {
|
|
3664
|
+
font-size: 12px;
|
|
3665
|
+
}
|
|
3666
|
+
|
|
3667
|
+
/* Center the Sparkles SVG with the label. Without this, the SVG inherits
|
|
3668
|
+
* baseline alignment from inline-flow and sits ~2px below the cap line. */
|
|
3669
|
+
.btn-suggest-template::part(prefix) {
|
|
3670
|
+
display: inline-flex;
|
|
3671
|
+
align-items: center;
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
.btn-suggest-template svg {
|
|
3675
|
+
display: block;
|
|
3676
|
+
}
|
|
3677
|
+
|
|
3678
|
+
/* Advisor dialog */
|
|
3679
|
+
.advisor-dialog::part(panel) {
|
|
3680
|
+
max-width: 560px;
|
|
3681
|
+
}
|
|
3682
|
+
|
|
3683
|
+
.advisor-dialog-body {
|
|
3684
|
+
font-size: 14px;
|
|
3685
|
+
color: inherit;
|
|
3686
|
+
line-height: 1.55;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
.advisor-loading {
|
|
3690
|
+
display: flex;
|
|
3691
|
+
align-items: center;
|
|
3692
|
+
gap: 10px;
|
|
3693
|
+
color: var(--muted);
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
.advisor-error {
|
|
3697
|
+
color: var(--sl-color-danger-600, #c41e3a);
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
.advisor-headline {
|
|
3701
|
+
display: flex;
|
|
3702
|
+
align-items: center;
|
|
3703
|
+
gap: 10px;
|
|
3704
|
+
margin-bottom: 8px;
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
.advisor-template-name {
|
|
3708
|
+
font-size: 16px;
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
.advisor-rationale {
|
|
3712
|
+
margin: 6px 0 12px;
|
|
3713
|
+
}
|
|
3714
|
+
|
|
3715
|
+
.advisor-alternatives ul {
|
|
3716
|
+
list-style: disc;
|
|
3717
|
+
padding-left: 20px;
|
|
3718
|
+
margin: 4px 0 0;
|
|
3719
|
+
}
|
|
3720
|
+
|
|
3721
|
+
.advisor-alternatives li {
|
|
3722
|
+
margin-bottom: 6px;
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
.advisor-alternatives .btn-advisor-pick-alt {
|
|
3726
|
+
margin-left: 8px;
|
|
3727
|
+
vertical-align: middle;
|
|
3728
|
+
}
|
|
3729
|
+
|
|
3640
3730
|
/* Plan file autocomplete */
|
|
3641
3731
|
.plan-autocomplete {
|
|
3642
3732
|
position: relative;
|
package/package.json
CHANGED
|
@@ -746,6 +746,76 @@ export function createTemplatesRoutes() {
|
|
|
746
746
|
(req, res) => handleImportPreview(req, res),
|
|
747
747
|
);
|
|
748
748
|
|
|
749
|
+
/**
|
|
750
|
+
* POST /api/projects/:projectId/templates/advise
|
|
751
|
+
* Body: { sourceType, sourceValue?, model? }
|
|
752
|
+
*
|
|
753
|
+
* Asks the Python advisor to pick the best-fit pipeline template for
|
|
754
|
+
* the given source. `sourceType` mirrors the launcher's options
|
|
755
|
+
* ("none" | "spec" | "source" | "pr", or "prompt"/"plan" when called
|
|
756
|
+
* directly). The route forwards "none" to the Python advisor as
|
|
757
|
+
* "prompt" so the prompt textarea path works unchanged.
|
|
758
|
+
*
|
|
759
|
+
* Returns { ok: true, advice: { template_id, rationale, confidence,
|
|
760
|
+
* alternatives: [...] } } on success.
|
|
761
|
+
*/
|
|
762
|
+
router.post('/templates/advise', (req, res) => {
|
|
763
|
+
const body = req.body || {};
|
|
764
|
+
const rawSourceType = String(body.sourceType || '').trim();
|
|
765
|
+
if (!rawSourceType) {
|
|
766
|
+
return res
|
|
767
|
+
.status(400)
|
|
768
|
+
.json({ ok: false, error: 'sourceType is required' });
|
|
769
|
+
}
|
|
770
|
+
// Launcher uses "none" for the prompt textarea path. Map it to the
|
|
771
|
+
// backend's `prompt` source so the same wire shape covers both.
|
|
772
|
+
const sourceType = rawSourceType === 'none' ? 'prompt' : rawSourceType;
|
|
773
|
+
const ALLOWED = new Set(['prompt', 'spec', 'source', 'pr', 'plan']);
|
|
774
|
+
if (!ALLOWED.has(sourceType)) {
|
|
775
|
+
return res.status(400).json({
|
|
776
|
+
ok: false,
|
|
777
|
+
error: `sourceType must be one of: ${Array.from(ALLOWED).join(', ')}`,
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
const sourceValue = String(body.sourceValue || '');
|
|
781
|
+
if (sourceType !== 'prompt' && !sourceValue.trim()) {
|
|
782
|
+
return res.status(400).json({
|
|
783
|
+
ok: false,
|
|
784
|
+
error: 'sourceValue is required for non-prompt sources',
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
if (sourceType === 'prompt' && !sourceValue.trim()) {
|
|
788
|
+
return res
|
|
789
|
+
.status(400)
|
|
790
|
+
.json({ ok: false, error: 'sourceValue (prompt text) is required' });
|
|
791
|
+
}
|
|
792
|
+
const model =
|
|
793
|
+
body.model && typeof body.model === 'string' ? body.model : 'sonnet';
|
|
794
|
+
const { projectRoot } = req.project;
|
|
795
|
+
try {
|
|
796
|
+
const stdout = runWorcaTemplates(
|
|
797
|
+
projectRoot,
|
|
798
|
+
[
|
|
799
|
+
'advise',
|
|
800
|
+
'--source-type',
|
|
801
|
+
sourceType,
|
|
802
|
+
'--source-value',
|
|
803
|
+
'-',
|
|
804
|
+
'--model',
|
|
805
|
+
model,
|
|
806
|
+
],
|
|
807
|
+
{ stdin: sourceValue, timeout: 90000 },
|
|
808
|
+
);
|
|
809
|
+
const advice = JSON.parse(stdout || '{}');
|
|
810
|
+
res.json({ ok: true, advice });
|
|
811
|
+
} catch (err) {
|
|
812
|
+
const status = statusForCliCode(err.cliCode);
|
|
813
|
+
res
|
|
814
|
+
.status(status === 500 ? 500 : status)
|
|
815
|
+
.json({ ok: false, error: err.message || 'advise failed' });
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
749
819
|
/**
|
|
750
820
|
* POST /api/projects/:projectId/templates/validate
|
|
751
821
|
* Body: { config }
|