mirror of
https://github.com/johndoe6345789/AutoMetabuilder.git
synced 2026-04-24 13:54:59 +00:00
Remove unused UI components: Delete unused atomic, molecular, and organism templates to streamline codebase and enhance maintainability.
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
{# Atomic UI primitives for Jinja2 templates. #}
|
||||
{% macro icon(name, extra_class='') -%}
|
||||
<i class="bi bi-{{ name }} {{ extra_class | e }}"></i>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro section_header(title, subtitle='') -%}
|
||||
<div class="amb-section-header">
|
||||
<h1>{{ title }}</h1>
|
||||
{% if subtitle %}
|
||||
<p>{{ subtitle }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro helper_text(text) -%}
|
||||
{% if text %}
|
||||
<p class="text-muted small mb-2">{{ text }}</p>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro pill(text, tone='neutral') -%}
|
||||
<span class="amb-pill amb-pill-{{ tone }}">{{ text }}</span>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,6 @@
|
||||
{# Helper text for form fields. #}
|
||||
{% macro helper_text(text) -%}
|
||||
{% if text %}
|
||||
<p class="text-muted small mb-2">{{ text }}</p>
|
||||
{% endif %}
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{# Atomic UI primitive for Jinja2 templates. #}
|
||||
{% macro icon(name, extra_class='') -%}
|
||||
<i class="bi bi-{{ name }} {{ extra_class | e }}"></i>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,4 @@
|
||||
{# Pill label. #}
|
||||
{% macro pill(text, tone='neutral') -%}
|
||||
<span class="amb-pill amb-pill-{{ tone }}">{{ text }}</span>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{# Section header macro. #}
|
||||
{% macro section_header(title, subtitle='') -%}
|
||||
<div class="amb-section-header">
|
||||
<h1>{{ title }}</h1>
|
||||
{% if subtitle %}
|
||||
<p>{{ subtitle }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{# Molecular UI components composed from atoms. #}
|
||||
{% from "components/atoms.html" import icon %}
|
||||
{# Card component. #}
|
||||
{% from "components/atoms/icon.html" import icon %}
|
||||
|
||||
{% macro card(title, icon_name=None, actions_html='') -%}
|
||||
<div class="amb-card">
|
||||
@@ -17,11 +17,3 @@
|
||||
</div>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro empty_state(icon_name, title, body) -%}
|
||||
<div class="amb-empty-state text-center text-muted py-5">
|
||||
{{ icon(icon_name, 'amb-empty-icon') }}
|
||||
<h6 class="mt-3">{{ title }}</h6>
|
||||
<p class="small mb-0">{{ body }}</p>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,10 @@
|
||||
{# Empty state component. #}
|
||||
{% from "components/atoms/icon.html" import icon %}
|
||||
|
||||
{% macro empty_state(icon_name, title, body) -%}
|
||||
<div class="amb-empty-state text-center text-muted py-5">
|
||||
{{ icon(icon_name, 'amb-empty-icon') }}
|
||||
<h6 class="mt-3">{{ title }}</h6>
|
||||
<p class="small mb-0">{{ body }}</p>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
@@ -1,5 +1,5 @@
|
||||
{# Organisms combine molecules into larger UI blocks. #}
|
||||
{% from "components/atoms.html" import icon %}
|
||||
{# Callout component. #}
|
||||
{% from "components/atoms/icon.html" import icon %}
|
||||
|
||||
{% macro callout(title, body, tone='info', icon_name='info-circle') -%}
|
||||
<div class="amb-callout amb-callout-{{ tone }}">
|
||||
@@ -1,516 +0,0 @@
|
||||
{# Page section components composed from atoms, molecules, and organisms. #}
|
||||
{% from "components/atoms.html" import section_header, helper_text, pill %}
|
||||
{% from "components/molecules.html" import card, empty_state %}
|
||||
{% from "components/organisms.html" import callout %}
|
||||
|
||||
{% macro choice_card(id, value, icon_name, title, description, checked=False, extra_html='') -%}
|
||||
<label class="amb-choice-card" for="{{ id }}">
|
||||
<input type="radio" name="mode" id="{{ id }}" value="{{ value }}" {% if checked %}checked{% endif %}>
|
||||
<div class="amb-choice-content">
|
||||
<div class="amb-choice-title">
|
||||
<i class="bi bi-{{ icon_name }}"></i> {{ title }}
|
||||
</div>
|
||||
<p class="amb-choice-text">{{ description }}</p>
|
||||
{% if extra_html %}{{ extra_html | safe }}{% endif %}
|
||||
</div>
|
||||
</label>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro prompt_chip(target_id, snippet, label) -%}
|
||||
<button type="button" class="amb-chip" data-prompt-target="{{ target_id }}"
|
||||
data-prompt-snippet="{{ snippet }}">{{ label }}</button>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro prompt_step(step_number, title, description, textarea_id, name, placeholder, value, rows=5, chips=[]) -%}
|
||||
<div class="amb-prompt-step">
|
||||
<div class="amb-prompt-step-title">
|
||||
<span class="amb-step-badge">{{ step_number }}</span> {{ title }}
|
||||
</div>
|
||||
{{ helper_text(description) }}
|
||||
<textarea name="{{ name }}" class="form-control" rows="{{ rows }}" id="{{ textarea_id }}"
|
||||
placeholder="{{ placeholder }}">{{ value }}</textarea>
|
||||
{% if chips %}
|
||||
<div class="amb-chip-row">
|
||||
{% for chip in chips %}
|
||||
{{ prompt_chip(chip.target, chip.snippet, chip.label) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro dashboard_section() -%}
|
||||
<section id="dashboard" class="amb-section active">
|
||||
{{ section_header(t('ui.dashboard.title', 'Dashboard'), t('ui.dashboard.subtitle', 'Control the bot and monitor system activity')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5 col-md-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-robot"></i> {{ t('ui.dashboard.bot_control', 'Bot Control') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<form action="/run" method="post" id="run-form">
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.dashboard.run_strategy', 'Run Strategy') }}</label>
|
||||
<div class="amb-choice-grid">
|
||||
{% set iterations_extra %}
|
||||
<div class="amb-choice-inline" id="iterations-group" style="display: none;">
|
||||
<span class="amb-choice-meta">{{ t('ui.dashboard.run.repeat.label', 'Iterations') }}</span>
|
||||
<input type="number" name="iterations" class="form-control form-control-sm"
|
||||
value="5" min="1" max="100">
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ choice_card('mode-once', 'once', '1-circle', t('ui.dashboard.run.single.title', 'Single Iteration'), t('ui.dashboard.run.single.desc', 'One full pass through the workflow.'), True) }}
|
||||
{{ choice_card('mode-iterations', 'iterations', 'arrow-repeat', t('ui.dashboard.run.repeat.title', 'Repeat'), t('ui.dashboard.run.repeat.desc', 'Run a fixed number of cycles.'), False, iterations_extra) }}
|
||||
{{ choice_card('mode-yolo', 'yolo', 'infinity', t('ui.dashboard.run.yolo.title', 'YOLO'), t('ui.dashboard.run.yolo.desc', 'Keep iterating until you stop it.')) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.dashboard.automation', 'Automation') }}</label>
|
||||
<div class="amb-toggle-stack">
|
||||
<div class="form-check form-switch amb-toggle">
|
||||
<input class="form-check-input" type="checkbox" name="yolo" id="yolo-check" value="true" checked>
|
||||
<label class="form-check-label" for="yolo-check">
|
||||
<strong>{{ t('ui.dashboard.auto_approve.title', 'Auto-approve tools') }}</strong>
|
||||
<small class="text-muted d-block">{{ t('ui.dashboard.auto_approve.desc', 'Skip confirmations so the bot can run faster.') }}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-switch amb-toggle">
|
||||
<input class="form-check-input" type="checkbox" name="stop_at_mvp" id="mvp-check" value="true">
|
||||
<label class="form-check-label" for="mvp-check">
|
||||
<strong>{{ t('ui.dashboard.stop_mvp.title', 'Stop at MVP') }}</strong>
|
||||
<small class="text-muted d-block">{{ t('ui.dashboard.stop_mvp.desc', 'Pause automatically when the MVP milestone is reached.') }}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="run-btn" type="submit" class="btn btn-danger w-100" {% if is_running %}disabled{% endif %}>
|
||||
<i class="bi bi-play-fill"></i> {{ t('ui.dashboard.start_bot', 'Start Bot') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-activity"></i> {{ t('ui.dashboard.status.title', 'Status') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<span>{{ t('ui.dashboard.status.bot_label', 'Bot Status') }}</span>
|
||||
<div id="status-indicator" class="amb-status {% if is_running %}amb-status-running{% else %}amb-status-idle{% endif %}">
|
||||
<span class="amb-status-dot"></span>
|
||||
{% if is_running %}{{ t('ui.dashboard.status.running', 'Running') }}{% else %}{{ t('ui.dashboard.status.idle', 'Idle') }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<span>{{ t('ui.dashboard.status.mvp_label', 'MVP Milestone') }}</span>
|
||||
<span id="mvp-badge" class="badge {% if mvp_reached %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{% if mvp_reached %}
|
||||
<i class="bi bi-check-circle-fill"></i> {{ t('ui.dashboard.status.mvp_reached', 'Reached') }}
|
||||
{% else %}
|
||||
<i class="bi bi-hourglass-split"></i> {{ t('ui.dashboard.status.mvp_progress', 'In Progress') }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div id="status-progress" class="progress" style="display: {% if is_running %}block{% else %}none{% endif %};">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7 col-md-6">
|
||||
<div class="amb-card" style="height: calc(100% - 1.5rem);">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-terminal"></i> {{ t('ui.dashboard.logs.title', 'Recent Logs') }}</h5>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="StatusPoller.refreshLogs()" title="{{ t('ui.actions.refresh', 'Refresh') }}">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="amb-card-body p-0">
|
||||
<pre id="logs" class="amb-logs m-0" style="height: 450px;">{{ logs }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro workflow_section() -%}
|
||||
<section id="workflow" class="amb-section">
|
||||
{{ section_header(t('ui.workflow.title', 'Workflow Builder'), t('ui.workflow.subtitle', "Design the bot's task execution pipeline")) }}
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-diagram-3"></i> {{ t('ui.workflow.card.title', 'Tasks & Steps') }}</h5>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-workflow-toggle>
|
||||
<i class="bi bi-code-slash"></i> {{ t('ui.workflow.toggle_json', 'Toggle JSON') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div id="workflow-builder" class="mb-3">
|
||||
<button type="button" class="btn btn-primary" data-workflow-fallback>
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.workflow.add_task', 'Add Task') }}
|
||||
</button>
|
||||
</div>
|
||||
<form action="/workflow" method="post" id="workflow-form">
|
||||
<textarea id="workflow-content" name="content" class="form-control amb-code-editor d-none" rows="15">{{ workflow_content }}</textarea>
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-save"></i> {{ t('ui.workflow.save', 'Save Workflow') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro prompt_section() -%}
|
||||
<section id="prompt" class="amb-section">
|
||||
{{ section_header(t('ui.prompt.title', 'Prompt Builder'), t('ui.prompt.subtitle', 'Shape how the assistant thinks, speaks, and decides')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
{% call card(t('ui.prompt.card.title', 'Prompt Builder'), "chat-square-text") %}
|
||||
<form action="/prompt" method="post" id="prompt-form">
|
||||
<input type="hidden" name="prompt_mode" id="prompt-mode" value="builder">
|
||||
<div id="prompt-builder">
|
||||
{% set system_chips = [
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.senior.snippet', 'You are a senior software engineer focused on correctness and clarity.'), 'label': t('ui.prompt.chip.senior.label', 'Senior engineer')},
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.ask.snippet', 'Ask clarifying questions before making risky changes.'), 'label': t('ui.prompt.chip.ask.label', 'Ask questions')},
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.minimal.snippet', 'Prefer minimal diffs and explain trade-offs.'), 'label': t('ui.prompt.chip.minimal.label', 'Minimal diffs')}
|
||||
] %}
|
||||
{{ prompt_step(1,
|
||||
t('ui.prompt.step1.title', 'Define the assistant'),
|
||||
t('ui.prompt.step1.desc', 'Describe who the assistant is and how it should behave.'),
|
||||
'system-prompt',
|
||||
'system_content',
|
||||
t('ui.prompt.step1.placeholder', 'You are a senior software engineer who prefers small, safe changes.'),
|
||||
(prompt_content | extract_system_content),
|
||||
6,
|
||||
system_chips
|
||||
) }}
|
||||
|
||||
{% set user_chips = [
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.ux.snippet', 'Focus on UX polish, and avoid major refactors.'), 'label': t('ui.prompt.chip.ux.label', 'UX polish')},
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.tests.snippet', 'Add tests when possible, but avoid heavy scaffolding.'), 'label': t('ui.prompt.chip.tests.label', 'Add tests')},
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.summarize.snippet', 'Summarize changes and suggest next steps.'), 'label': t('ui.prompt.chip.summarize.label', 'Summarize')}
|
||||
] %}
|
||||
{{ prompt_step(2,
|
||||
t('ui.prompt.step2.title', 'Give the mission'),
|
||||
t('ui.prompt.step2.desc', 'Explain what the bot should accomplish right now.'),
|
||||
'user-prompt',
|
||||
'user_content',
|
||||
t('ui.prompt.step2.placeholder', 'Review the repo, improve the UI, and summarize what changed.'),
|
||||
(prompt_content | extract_user_content),
|
||||
5,
|
||||
user_chips
|
||||
) }}
|
||||
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.prompt.model.label', 'Choose a model') }}</label>
|
||||
{{ helper_text(t('ui.prompt.model.desc', 'Pick the balance of quality and speed that fits the task.')) }}
|
||||
<select name="model" class="form-select" data-choices>
|
||||
<option value="openai/gpt-4o" {% if 'gpt-4o' in prompt_content %}selected{% endif %}>GPT-4o ({{ t('ui.prompt.model.recommended', 'Recommended') }})</option>
|
||||
<option value="openai/gpt-4o-mini" {% if 'gpt-4o-mini' in prompt_content %}selected{% endif %}>GPT-4o Mini ({{ t('ui.prompt.model.faster', 'Faster') }})</option>
|
||||
<option value="openai/gpt-4-turbo" {% if 'gpt-4-turbo' in prompt_content %}selected{% endif %}>GPT-4 Turbo</option>
|
||||
<option value="anthropic/claude-3-5-sonnet" {% if 'claude' in prompt_content %}selected{% endif %}>Claude 3.5 Sonnet</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="prompt-raw" class="d-none">
|
||||
<label class="amb-form-label">{{ t('ui.prompt.raw.label', 'Advanced YAML') }}</label>
|
||||
{{ helper_text(t('ui.prompt.raw.desc', 'Edit the full YAML only if you need fine control.')) }}
|
||||
<textarea name="content" class="amb-code-editor" id="prompt-yaml" rows="18">{{ prompt_content }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success" onclick="buildPromptYaml()">
|
||||
<i class="bi bi-save"></i> {{ t('ui.prompt.save', 'Save Prompt') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="toggleRawPrompt()">
|
||||
<i class="bi bi-code-slash"></i> {{ t('ui.prompt.advanced_yaml', 'Advanced YAML') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
{% call card(t('ui.prompt.guidance.title', 'Guidance'), "lightbulb") %}
|
||||
<div class="amb-guidance">
|
||||
<div class="mb-3">
|
||||
<strong>{{ t('ui.prompt.guidance.keep_human.title', 'Keep it human') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.keep_human.desc', 'Write instructions the way you would brief a teammate.') }}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>{{ t('ui.prompt.guidance.be_specific.title', 'Be specific') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.be_specific.desc', 'Mention constraints like time, scope, or testing expectations.') }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ t('ui.prompt.guidance.raw.title', 'Use advanced YAML sparingly') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.raw.desc', 'Only switch to raw YAML if you need full control.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro settings_section() -%}
|
||||
<section id="settings" class="amb-section">
|
||||
{{ section_header(t('ui.settings.title', 'Settings'), t('ui.settings.subtitle', 'Configure services, security, and environment preferences')) }}
|
||||
|
||||
{{ callout(t('ui.settings.callout.title', 'Human-friendly settings'), t('ui.settings.callout.body', 'Each field explains what it does. Add descriptions in metadata.json to keep custom settings readable for everyone.'), "info", "info-circle") }}
|
||||
|
||||
<form action="/settings" method="post" id="settings-form">
|
||||
<div class="row">
|
||||
{% set settings_desc = metadata.get('settings_descriptions', {}) %}
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-key"></i> {{ t('ui.settings.api_keys', 'API Keys') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['GITHUB_TOKEN', 'OPENAI_API_KEY', 'LITELLM_API_KEY'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">
|
||||
{{ desc.get('label', key) }}
|
||||
{% if desc.get('required') %}<span class="amb-required">*</span>{% endif %}
|
||||
</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, '') }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-gear"></i> {{ t('ui.settings.configuration', 'Configuration') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['GITHUB_REPOSITORY', 'LOG_LEVEL', 'APP_LANG', 'PROMPT_PATH'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
{% if desc.get('type') == 'select' %}
|
||||
<select name="env_{{ key }}" class="form-select" data-choices>
|
||||
<option value="">{{ t('ui.common.select_placeholder', 'Select...') }}</option>
|
||||
{% for opt in desc.get('options', []) %}
|
||||
<option value="{{ opt }}" {% if env_vars.get(key) == opt %}selected{% endif %}>{{ opt }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, desc.get('default', '')) }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-shield-lock"></i> {{ t('ui.settings.web_access', 'Web UI Access') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['WEB_USER', 'WEB_PASSWORD'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, '') }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-sliders"></i> {{ t('ui.settings.other', 'Other Settings') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div class="row">
|
||||
{% set known_keys = ['GITHUB_TOKEN', 'OPENAI_API_KEY', 'LITELLM_API_KEY', 'GITHUB_REPOSITORY', 'LOG_LEVEL', 'APP_LANG', 'PROMPT_PATH', 'WEB_USER', 'WEB_PASSWORD'] %}
|
||||
{% for key, value in env_vars.items() %}
|
||||
{% if key not in known_keys %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="amb-form-group">
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">
|
||||
{{ desc.get('description', t('ui.settings.custom_default_desc', 'Custom environment setting. Add a description in metadata.json to show it here.')) }}
|
||||
</p>
|
||||
<input type="text" name="env_{{ key }}" class="form-control" value="{{ value }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.settings.add.title', 'Add New Setting') }}</label>
|
||||
<p class="text-muted small mb-2">{{ t('ui.settings.add.desc', 'Use uppercase keys with underscores, like API_TIMEOUT.') }}</p>
|
||||
<div class="input-group">
|
||||
<input type="text" name="new_env_key" class="form-control" placeholder="{{ t('ui.settings.add.placeholder_key', 'KEY') }}">
|
||||
<input type="text" name="new_env_value" class="form-control" placeholder="{{ t('ui.settings.add.placeholder_value', 'Value') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-footer">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-save"></i> {{ t('ui.settings.save_all', 'Save All Settings') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro translations_section() -%}
|
||||
<section id="translations" class="amb-section">
|
||||
{{ section_header(t('ui.translations.title', 'Translations'), t('ui.translations.subtitle', 'Create, edit, and maintain language files for bot messages')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-globe"></i> {{ t('ui.translations.languages', 'Languages') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body p-0">
|
||||
<div class="list-group list-group-flush" id="translation-list">
|
||||
{% for lang, file in translations.items() %}
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center translation-item" data-lang="{{ lang }}">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-file-text"></i>
|
||||
<div>
|
||||
<strong>{{ lang.upper() }}</strong>
|
||||
<small class="text-muted d-block">{{ file }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-primary" onclick="TranslationEditor.load('{{ lang }}')" title="{{ t('ui.actions.edit', 'Edit') }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
{% if lang != 'en' %}
|
||||
<button type="button" class="btn btn-outline-danger" onclick="TranslationEditor.delete('{{ lang }}')" title="{{ t('ui.actions.delete', 'Delete') }}">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-footer">
|
||||
<form action="/translations" method="post" class="d-flex gap-2">
|
||||
<select name="lang" class="form-select form-select-sm" data-choices data-placeholder="{{ t('ui.translations.add_language_placeholder', 'Add language...') }}" required>
|
||||
<option value="">{{ t('ui.translations.select_language_placeholder', 'Select language...') }}</option>
|
||||
{% for lang in metadata.suggestions.languages %}
|
||||
{% if lang not in translations %}
|
||||
<option value="{{ lang }}">{{ lang }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.actions.add', 'Add') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-pencil-square"></i> <span id="editor-title">{{ t('ui.translations.editor.title', 'Translation Editor') }}</span></h5>
|
||||
<div id="editor-actions" class="amb-translation-actions" style="display: none;">
|
||||
<span id="missing-count" class="amb-pill amb-pill-warning">{{ t('ui.translations.missing_count', '{count} missing') | replace('{count}', '0') }}</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.fillMissing()">
|
||||
<i class="bi bi-magic"></i> {{ t('ui.translations.actions.fill_missing', 'Fill Missing') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.reset()">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> {{ t('ui.actions.reset', 'Reset') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-success" onclick="TranslationEditor.save()">
|
||||
<i class="bi bi-save"></i> {{ t('ui.actions.save', 'Save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div id="translation-editor-placeholder">
|
||||
{{ empty_state("translate", t('ui.translations.empty.title', 'Pick a language'), t('ui.translations.empty.body', 'Select a language from the list to start editing translations.')) }}
|
||||
</div>
|
||||
<div id="translation-editor" style="display: none;">
|
||||
<div class="amb-translation-toolbar">
|
||||
<div class="amb-translation-search">
|
||||
<input type="search" class="form-control form-control-sm" id="translation-search"
|
||||
placeholder="{{ t('ui.translations.search.placeholder', 'Search keys or text...') }}" oninput="TranslationEditor.filter(this.value)">
|
||||
</div>
|
||||
<div class="form-check form-switch amb-translation-toggle">
|
||||
<input class="form-check-input" type="checkbox" id="translation-missing-toggle" checked
|
||||
onchange="TranslationEditor.toggleMissing(this.checked)">
|
||||
<label class="form-check-label small" for="translation-missing-toggle">{{ t('ui.translations.toggle_missing', 'Show missing keys') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-translation-add">
|
||||
<input type="text" class="form-control form-control-sm" id="new-translation-key" placeholder="{{ t('ui.translations.add.key_placeholder', 'new.key') }}">
|
||||
<input type="text" class="form-control form-control-sm" id="new-translation-value" placeholder="{{ t('ui.translations.add.value_placeholder', 'Translation text') }}">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.prefillNewValue()">
|
||||
{{ t('ui.translations.add.use_english', 'Use English') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="TranslationEditor.addEntry()">
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.translations.add.add_key', 'Add Key') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm" id="translation-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30%;">{{ t('ui.translations.table.key', 'Key') }}</th>
|
||||
<th>{{ t('ui.translations.table.translation', 'Translation') }}</th>
|
||||
<th style="width: 70px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,13 @@
|
||||
{# Choice card macro. #}
|
||||
{% macro choice_card(id, value, icon_name, title, description, checked=False, extra_html='') -%}
|
||||
<label class="amb-choice-card" for="{{ id }}">
|
||||
<input type="radio" name="mode" id="{{ id }}" value="{{ value }}" {% if checked %}checked{% endif %}>
|
||||
<div class="amb-choice-content">
|
||||
<div class="amb-choice-title">
|
||||
<i class="bi bi-{{ icon_name }}"></i> {{ title }}
|
||||
</div>
|
||||
<p class="amb-choice-text">{{ description }}</p>
|
||||
{% if extra_html %}{{ extra_html | safe }}{% endif %}
|
||||
</div>
|
||||
</label>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,107 @@
|
||||
{# Dashboard section macro. #}
|
||||
{% from "components/atoms/section_header.html" import section_header %}
|
||||
{% from "components/sections/choice_card.html" import choice_card %}
|
||||
|
||||
{% macro dashboard_section() -%}
|
||||
<section id="dashboard" class="amb-section active">
|
||||
{{ section_header(t('ui.dashboard.title', 'Dashboard'), t('ui.dashboard.subtitle', 'Control the bot and monitor system activity')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-5 col-md-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-robot"></i> {{ t('ui.dashboard.bot_control', 'Bot Control') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<form action="/run" method="post" id="run-form">
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.dashboard.run_strategy', 'Run Strategy') }}</label>
|
||||
<div class="amb-choice-grid">
|
||||
{% set iterations_extra %}
|
||||
<div class="amb-choice-inline" id="iterations-group" style="display: none;">
|
||||
<span class="amb-choice-meta">{{ t('ui.dashboard.run.repeat.label', 'Iterations') }}</span>
|
||||
<input type="number" name="iterations" class="form-control form-control-sm"
|
||||
value="5" min="1" max="100">
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{{ choice_card('mode-once', 'once', '1-circle', t('ui.dashboard.run.single.title', 'Single Iteration'), t('ui.dashboard.run.single.desc', 'One full pass through the workflow.'), True) }}
|
||||
{{ choice_card('mode-iterations', 'iterations', 'arrow-repeat', t('ui.dashboard.run.repeat.title', 'Repeat'), t('ui.dashboard.run.repeat.desc', 'Run a fixed number of cycles.'), False, iterations_extra) }}
|
||||
{{ choice_card('mode-yolo', 'yolo', 'infinity', t('ui.dashboard.run.yolo.title', 'YOLO'), t('ui.dashboard.run.yolo.desc', 'Keep iterating until you stop it.')) }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.dashboard.automation', 'Automation') }}</label>
|
||||
<div class="amb-toggle-stack">
|
||||
<div class="form-check form-switch amb-toggle">
|
||||
<input class="form-check-input" type="checkbox" name="yolo" id="yolo-check" value="true" checked>
|
||||
<label class="form-check-label" for="yolo-check">
|
||||
<strong>{{ t('ui.dashboard.auto_approve.title', 'Auto-approve tools') }}</strong>
|
||||
<small class="text-muted d-block">{{ t('ui.dashboard.auto_approve.desc', 'Skip confirmations so the bot can run faster.') }}</small>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-switch amb-toggle">
|
||||
<input class="form-check-input" type="checkbox" name="stop_at_mvp" id="mvp-check" value="true">
|
||||
<label class="form-check-label" for="mvp-check">
|
||||
<strong>{{ t('ui.dashboard.stop_mvp.title', 'Stop at MVP') }}</strong>
|
||||
<small class="text-muted d-block">{{ t('ui.dashboard.stop_mvp.desc', 'Pause automatically when the MVP milestone is reached.') }}</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="run-btn" type="submit" class="btn btn-danger w-100" {% if is_running %}disabled{% endif %}>
|
||||
<i class="bi bi-play-fill"></i> {{ t('ui.dashboard.start_bot', 'Start Bot') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-activity"></i> {{ t('ui.dashboard.status.title', 'Status') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div class="d-flex flex-column gap-3">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<span>{{ t('ui.dashboard.status.bot_label', 'Bot Status') }}</span>
|
||||
<div id="status-indicator" class="amb-status {% if is_running %}amb-status-running{% else %}amb-status-idle{% endif %}">
|
||||
<span class="amb-status-dot"></span>
|
||||
{% if is_running %}{{ t('ui.dashboard.status.running', 'Running') }}{% else %}{{ t('ui.dashboard.status.idle', 'Idle') }}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<span>{{ t('ui.dashboard.status.mvp_label', 'MVP Milestone') }}</span>
|
||||
<span id="mvp-badge" class="badge {% if mvp_reached %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{% if mvp_reached %}
|
||||
<i class="bi bi-check-circle-fill"></i> {{ t('ui.dashboard.status.mvp_reached', 'Reached') }}
|
||||
{% else %}
|
||||
<i class="bi bi-hourglass-split"></i> {{ t('ui.dashboard.status.mvp_progress', 'In Progress') }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<div id="status-progress" class="progress" style="display: {% if is_running %}block{% else %}none{% endif %};">
|
||||
<div class="progress-bar progress-bar-striped progress-bar-animated" style="width: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7 col-md-6">
|
||||
<div class="amb-card" style="height: calc(100% - 1.5rem);">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-terminal"></i> {{ t('ui.dashboard.logs.title', 'Recent Logs') }}</h5>
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="StatusPoller.refreshLogs()" title="{{ t('ui.actions.refresh', 'Refresh') }}">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="amb-card-body p-0">
|
||||
<pre id="logs" class="amb-logs m-0" style="height: 450px;">{{ logs }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,5 @@
|
||||
{# Prompt chip macro. #}
|
||||
{% macro prompt_chip(target_id, snippet, label) -%}
|
||||
<button type="button" class="amb-chip" data-prompt-target="{{ target_id }}"
|
||||
data-prompt-snippet="{{ snippet | e }}">{{ label }}</button>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,99 @@
|
||||
{# Prompt section macro. #}
|
||||
{% from "components/atoms/section_header.html" import section_header %}
|
||||
{% from "components/atoms/helper_text.html" import helper_text %}
|
||||
{% from "components/molecules/card.html" import card %}
|
||||
{% from "components/sections/prompt_step.html" import prompt_step %}
|
||||
|
||||
{% macro prompt_section() -%}
|
||||
<section id="prompt" class="amb-section">
|
||||
{{ section_header(t('ui.prompt.title', 'Prompt Builder'), t('ui.prompt.subtitle', 'Shape how the assistant thinks, speaks, and decides')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
{% call card(t('ui.prompt.card.title', 'Prompt Builder'), "chat-square-text") %}
|
||||
<form action="/prompt" method="post" id="prompt-form">
|
||||
<input type="hidden" name="prompt_mode" id="prompt-mode" value="builder">
|
||||
<div id="prompt-builder">
|
||||
{% set system_chips = [
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.senior.snippet', 'You are a senior software engineer focused on correctness and clarity.'), 'label': t('ui.prompt.chip.senior.label', 'Senior engineer')},
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.ask.snippet', 'Ask clarifying questions before making risky changes.'), 'label': t('ui.prompt.chip.ask.label', 'Ask questions')},
|
||||
{'target': 'system-prompt', 'snippet': t('ui.prompt.chip.minimal.snippet', 'Prefer minimal diffs and explain trade-offs.'), 'label': t('ui.prompt.chip.minimal.label', 'Minimal diffs')}
|
||||
] %}
|
||||
{{ prompt_step(1,
|
||||
t('ui.prompt.step1.title', 'Define the assistant'),
|
||||
t('ui.prompt.step1.desc', 'Describe who the assistant is and how it should behave.'),
|
||||
'system-prompt',
|
||||
'system_content',
|
||||
t('ui.prompt.step1.placeholder', 'You are a senior software engineer who prefers small, safe changes.'),
|
||||
(prompt_content | extract_system_content),
|
||||
6,
|
||||
system_chips
|
||||
) }}
|
||||
|
||||
{% set user_chips = [
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.ux.snippet', 'Focus on UX polish, and avoid major refactors.'), 'label': t('ui.prompt.chip.ux.label', 'UX polish')},
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.tests.snippet', 'Add tests when possible, but avoid heavy scaffolding.'), 'label': t('ui.prompt.chip.tests.label', 'Add tests')},
|
||||
{'target': 'user-prompt', 'snippet': t('ui.prompt.chip.summarize.snippet', 'Summarize changes and suggest next steps.'), 'label': t('ui.prompt.chip.summarize.label', 'Summarize')}
|
||||
] %}
|
||||
{{ prompt_step(2,
|
||||
t('ui.prompt.step2.title', 'Give the mission'),
|
||||
t('ui.prompt.step2.desc', 'Explain what the bot should accomplish right now.'),
|
||||
'user-prompt',
|
||||
'user_content',
|
||||
t('ui.prompt.step2.placeholder', 'Review the repo, improve the UI, and summarize what changed.'),
|
||||
(prompt_content | extract_user_content),
|
||||
5,
|
||||
user_chips
|
||||
) }}
|
||||
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.prompt.model.label', 'Choose a model') }}</label>
|
||||
{{ helper_text(t('ui.prompt.model.desc', 'Pick the balance of quality and speed that fits the task.')) }}
|
||||
<select name="model" class="form-select" data-choices>
|
||||
<option value="openai/gpt-4o" {% if 'gpt-4o' in prompt_content %}selected{% endif %}>GPT-4o ({{ t('ui.prompt.model.recommended', 'Recommended') }})</option>
|
||||
<option value="openai/gpt-4o-mini" {% if 'gpt-4o-mini' in prompt_content %}selected{% endif %}>GPT-4o Mini ({{ t('ui.prompt.model.faster', 'Faster') }})</option>
|
||||
<option value="openai/gpt-4-turbo" {% if 'gpt-4-turbo' in prompt_content %}selected{% endif %}>GPT-4 Turbo</option>
|
||||
<option value="anthropic/claude-3-5-sonnet" {% if 'claude' in prompt_content %}selected{% endif %}>Claude 3.5 Sonnet</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="prompt-raw" class="d-none">
|
||||
<label class="amb-form-label">{{ t('ui.prompt.raw.label', 'Advanced YAML') }}</label>
|
||||
{{ helper_text(t('ui.prompt.raw.desc', 'Edit the full YAML only if you need fine control.')) }}
|
||||
<textarea name="content" class="amb-code-editor" id="prompt-yaml" rows="18">{{ prompt_content }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="submit" class="btn btn-success" onclick="buildPromptYaml()">
|
||||
<i class="bi bi-save"></i> {{ t('ui.prompt.save', 'Save Prompt') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary" onclick="toggleRawPrompt()">
|
||||
<i class="bi bi-code-slash"></i> {{ t('ui.prompt.advanced_yaml', 'Advanced YAML') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endcall %}
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
{% call card(t('ui.prompt.guidance.title', 'Guidance'), "lightbulb") %}
|
||||
<div class="amb-guidance">
|
||||
<div class="mb-3">
|
||||
<strong>{{ t('ui.prompt.guidance.keep_human.title', 'Keep it human') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.keep_human.desc', 'Write instructions the way you would brief a teammate.') }}</p>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<strong>{{ t('ui.prompt.guidance.be_specific.title', 'Be specific') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.be_specific.desc', 'Mention constraints like time, scope, or testing expectations.') }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<strong>{{ t('ui.prompt.guidance.raw.title', 'Use advanced YAML sparingly') }}</strong>
|
||||
<p class="small text-muted mb-0">{{ t('ui.prompt.guidance.raw.desc', 'Only switch to raw YAML if you need full control.') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,21 @@
|
||||
{# Prompt step macro. #}
|
||||
{% from "components/atoms/helper_text.html" import helper_text %}
|
||||
{% from "components/sections/prompt_chip.html" import prompt_chip %}
|
||||
|
||||
{% macro prompt_step(step_number, title, description, textarea_id, name, placeholder, value, rows=5, chips=[]) -%}
|
||||
<div class="amb-prompt-step">
|
||||
<div class="amb-prompt-step-title">
|
||||
<span class="amb-step-badge">{{ step_number }}</span> {{ title }}
|
||||
</div>
|
||||
{{ helper_text(description) }}
|
||||
<textarea name="{{ name }}" class="form-control" rows="{{ rows }}" id="{{ textarea_id }}"
|
||||
placeholder="{{ placeholder }}">{{ value }}</textarea>
|
||||
{% if chips %}
|
||||
<div class="amb-chip-row">
|
||||
{% for chip in chips %}
|
||||
{{ prompt_chip(chip.target, chip.snippet, chip.label) }}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,133 @@
|
||||
{# Settings section macro. #}
|
||||
{% from "components/atoms/section_header.html" import section_header %}
|
||||
{% from "components/organisms/callout.html" import callout %}
|
||||
|
||||
{% macro settings_section() -%}
|
||||
<section id="settings" class="amb-section">
|
||||
{{ section_header(t('ui.settings.title', 'Settings'), t('ui.settings.subtitle', 'Configure services, security, and environment preferences')) }}
|
||||
|
||||
{{ callout(t('ui.settings.callout.title', 'Human-friendly settings'), t('ui.settings.callout.body', 'Each field explains what it does. Add descriptions in metadata.json to keep custom settings readable for everyone.'), "info", "info-circle") }}
|
||||
|
||||
<form action="/settings" method="post" id="settings-form">
|
||||
<div class="row">
|
||||
{% set settings_desc = metadata.get('settings_descriptions', {}) %}
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-key"></i> {{ t('ui.settings.api_keys', 'API Keys') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['GITHUB_TOKEN', 'OPENAI_API_KEY', 'LITELLM_API_KEY'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">
|
||||
{{ desc.get('label', key) }}
|
||||
{% if desc.get('required') %}<span class="amb-required">*</span>{% endif %}
|
||||
</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, '') }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-gear"></i> {{ t('ui.settings.configuration', 'Configuration') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['GITHUB_REPOSITORY', 'LOG_LEVEL', 'APP_LANG', 'PROMPT_PATH'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
{% if desc.get('type') == 'select' %}
|
||||
<select name="env_{{ key }}" class="form-select" data-choices>
|
||||
<option value="">{{ t('ui.common.select_placeholder', 'Select...') }}</option>
|
||||
{% for opt in desc.get('options', []) %}
|
||||
<option value="{{ opt }}" {% if env_vars.get(key) == opt %}selected{% endif %}>{{ opt }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, desc.get('default', '')) }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-shield-lock"></i> {{ t('ui.settings.web_access', 'Web UI Access') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
{% for key in ['WEB_USER', 'WEB_PASSWORD'] %}
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">{{ desc.get('description', '') }}</p>
|
||||
<input type="{{ desc.get('type', 'text') }}"
|
||||
name="env_{{ key }}"
|
||||
class="form-control"
|
||||
value="{{ env_vars.get(key, '') }}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-sliders"></i> {{ t('ui.settings.other', 'Other Settings') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div class="row">
|
||||
{% set known_keys = ['GITHUB_TOKEN', 'OPENAI_API_KEY', 'LITELLM_API_KEY', 'GITHUB_REPOSITORY', 'LOG_LEVEL', 'APP_LANG', 'PROMPT_PATH', 'WEB_USER', 'WEB_PASSWORD'] %}
|
||||
{% for key, value in env_vars.items() %}
|
||||
{% if key not in known_keys %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="amb-form-group">
|
||||
{% set desc = settings_desc.get(key, {}) %}
|
||||
<label class="amb-form-label">{{ desc.get('label', key) }}</label>
|
||||
<p class="text-muted small mb-2">
|
||||
{{ desc.get('description', t('ui.settings.custom_default_desc', 'Custom environment setting. Add a description in metadata.json to show it here.')) }}
|
||||
</p>
|
||||
<input type="text" name="env_{{ key }}" class="form-control" value="{{ value }}"
|
||||
placeholder="{{ desc.get('placeholder', '') }}">
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="amb-form-group">
|
||||
<label class="amb-form-label">{{ t('ui.settings.add.title', 'Add New Setting') }}</label>
|
||||
<p class="text-muted small mb-2">{{ t('ui.settings.add.desc', 'Use uppercase keys with underscores, like API_TIMEOUT.') }}</p>
|
||||
<div class="input-group">
|
||||
<input type="text" name="new_env_key" class="form-control" placeholder="{{ t('ui.settings.add.placeholder_key', 'KEY') }}">
|
||||
<input type="text" name="new_env_value" class="form-control" placeholder="{{ t('ui.settings.add.placeholder_value', 'Value') }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-footer">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-save"></i> {{ t('ui.settings.save_all', 'Save All Settings') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,119 @@
|
||||
{# Translations section macro. #}
|
||||
{% from "components/atoms/section_header.html" import section_header %}
|
||||
{% from "components/molecules/empty_state.html" import empty_state %}
|
||||
|
||||
{% macro translations_section() -%}
|
||||
<section id="translations" class="amb-section">
|
||||
{{ section_header(t('ui.translations.title', 'Translations'), t('ui.translations.subtitle', 'Create, edit, and maintain language files for bot messages')) }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-globe"></i> {{ t('ui.translations.languages', 'Languages') }}</h5>
|
||||
</div>
|
||||
<div class="amb-card-body p-0">
|
||||
<div class="list-group list-group-flush" id="translation-list">
|
||||
{% for lang, file in translations.items() %}
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center translation-item" data-lang="{{ lang }}">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<i class="bi bi-file-text"></i>
|
||||
<div>
|
||||
<strong>{{ lang.upper() }}</strong>
|
||||
<small class="text-muted d-block">{{ file }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button type="button" class="btn btn-outline-primary" onclick="TranslationEditor.load('{{ lang }}')" title="{{ t('ui.actions.edit', 'Edit') }}">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
{% if lang != 'en' %}
|
||||
<button type="button" class="btn btn-outline-danger" onclick="TranslationEditor.delete('{{ lang }}')" title="{{ t('ui.actions.delete', 'Delete') }}">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-footer">
|
||||
<form action="/translations" method="post" class="d-flex gap-2">
|
||||
<select name="lang" class="form-select form-select-sm" data-choices data-placeholder="{{ t('ui.translations.add_language_placeholder', 'Add language...') }}" required>
|
||||
<option value="">{{ t('ui.translations.select_language_placeholder', 'Select language...') }}</option>
|
||||
{% for lang in metadata.suggestions.languages %}
|
||||
{% if lang not in translations %}
|
||||
<option value="{{ lang }}">{{ lang }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<button type="submit" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.actions.add', 'Add') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-pencil-square"></i> <span id="editor-title">{{ t('ui.translations.editor.title', 'Translation Editor') }}</span></h5>
|
||||
<div id="editor-actions" class="amb-translation-actions" style="display: none;">
|
||||
<span id="missing-count" class="amb-pill amb-pill-warning">{{ t('ui.translations.missing_count', '{count} missing') | replace('{count}', '0') }}</span>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.fillMissing()">
|
||||
<i class="bi bi-magic"></i> {{ t('ui.translations.actions.fill_missing', 'Fill Missing') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.reset()">
|
||||
<i class="bi bi-arrow-counterclockwise"></i> {{ t('ui.actions.reset', 'Reset') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-success" onclick="TranslationEditor.save()">
|
||||
<i class="bi bi-save"></i> {{ t('ui.actions.save', 'Save') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div id="translation-editor-placeholder">
|
||||
{{ empty_state("translate", t('ui.translations.empty.title', 'Pick a language'), t('ui.translations.empty.body', 'Select a language from the list to start editing translations.')) }}
|
||||
</div>
|
||||
<div id="translation-editor" style="display: none;">
|
||||
<div class="amb-translation-toolbar">
|
||||
<div class="amb-translation-search">
|
||||
<input type="search" class="form-control form-control-sm" id="translation-search"
|
||||
placeholder="{{ t('ui.translations.search.placeholder', 'Search keys or text...') }}" oninput="TranslationEditor.filter(this.value)">
|
||||
</div>
|
||||
<div class="form-check form-switch amb-translation-toggle">
|
||||
<input class="form-check-input" type="checkbox" id="translation-missing-toggle" checked
|
||||
onchange="TranslationEditor.toggleMissing(this.checked)">
|
||||
<label class="form-check-label small" for="translation-missing-toggle">{{ t('ui.translations.toggle_missing', 'Show missing keys') }}</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="amb-translation-add">
|
||||
<input type="text" class="form-control form-control-sm" id="new-translation-key" placeholder="{{ t('ui.translations.add.key_placeholder', 'new.key') }}">
|
||||
<input type="text" class="form-control form-control-sm" id="new-translation-value" placeholder="{{ t('ui.translations.add.value_placeholder', 'Translation text') }}">
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" onclick="TranslationEditor.prefillNewValue()">
|
||||
{{ t('ui.translations.add.use_english', 'Use English') }}
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-primary" onclick="TranslationEditor.addEntry()">
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.translations.add.add_key', 'Add Key') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm" id="translation-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 30%;">{{ t('ui.translations.table.key', 'Key') }}</th>
|
||||
<th>{{ t('ui.translations.table.translation', 'Translation') }}</th>
|
||||
<th style="width: 70px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -0,0 +1,34 @@
|
||||
{# Workflow section macro. #}
|
||||
{% from "components/atoms/section_header.html" import section_header %}
|
||||
|
||||
{% macro workflow_section() -%}
|
||||
<section id="workflow" class="amb-section">
|
||||
{{ section_header(t('ui.workflow.title', 'Workflow Builder'), t('ui.workflow.subtitle', "Design the bot's task execution pipeline")) }}
|
||||
|
||||
<div class="amb-card">
|
||||
<div class="amb-card-header">
|
||||
<h5><i class="bi bi-diagram-3"></i> {{ t('ui.workflow.card.title', 'Tasks & Steps') }}</h5>
|
||||
<div>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary" data-workflow-toggle>
|
||||
<i class="bi bi-code-slash"></i> {{ t('ui.workflow.toggle_json', 'Toggle JSON') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="amb-card-body">
|
||||
<div id="workflow-builder" class="mb-3">
|
||||
<button type="button" class="btn btn-primary" data-workflow-fallback>
|
||||
<i class="bi bi-plus-lg"></i> {{ t('ui.workflow.add_task', 'Add Task') }}
|
||||
</button>
|
||||
</div>
|
||||
<form action="/workflow" method="post" id="workflow-form">
|
||||
<textarea id="workflow-content" name="content" class="form-control amb-code-editor d-none" rows="15">{{ workflow_content }}</textarea>
|
||||
<div class="d-flex gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-success">
|
||||
<i class="bi bi-save"></i> {{ t('ui.workflow.save', 'Save Workflow') }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{%- endmacro %}
|
||||
@@ -1,12 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% import "components/sections.html" as sections with context %}
|
||||
{% from "components/sections/dashboard_section.html" import dashboard_section with context %}
|
||||
{% from "components/sections/workflow_section.html" import workflow_section with context %}
|
||||
{% from "components/sections/prompt_section.html" import prompt_section with context %}
|
||||
{% from "components/sections/settings_section.html" import settings_section with context %}
|
||||
{% from "components/sections/translations_section.html" import translations_section with context %}
|
||||
|
||||
{% block content %}
|
||||
{{ sections.dashboard_section() }}
|
||||
{{ sections.workflow_section() }}
|
||||
{{ sections.prompt_section() }}
|
||||
{{ sections.settings_section() }}
|
||||
{{ sections.translations_section() }}
|
||||
{{ dashboard_section() }}
|
||||
{{ workflow_section() }}
|
||||
{{ prompt_section() }}
|
||||
{{ settings_section() }}
|
||||
{{ translations_section() }}
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
|
||||
Reference in New Issue
Block a user