Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ repos:
language: system
pass_filenames: false
files: '^(internal/(auditlog|core|providers|server|usage)/.*\.go|tests/perf/.*\.go|Makefile|\.github/workflows/test\.yml)$'

# 3. High-Performance Go Linter (golangci-lint)
- repo: https://github.com/golangci/golangci-lint
rev: v2.7.2 # Latest stable version (Dec 2025)
hooks:
- id: golangci-lint
args: [--fix=false, --timeout=5m] # Optional: increase timeout if project is large
- id: go-lint
name: make lint
entry: make lint
language: system
pass_filenames: false
files: '^(\.golangci\.yml|Makefile|\.github/workflows/test\.yml|.*\.go)$'
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ swagger:

# Run linter
lint:
golangci-lint run ./...
golangci-lint run ./cmd/... ./config/... ./internal/...
golangci-lint run --build-tags=e2e ./tests/e2e/...
golangci-lint run --build-tags=integration ./tests/integration/...
golangci-lint run --build-tags=contract ./tests/contract/...

# Run linter with auto-fix
lint-fix:
golangci-lint run --fix ./...
golangci-lint run --fix ./cmd/... ./config/... ./internal/...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Keep lint-fix scope aligned with lint.

Line 93 only auto-fixes core paths, so make lint-fix && make lint can still fail on tagged test trees.

Proposed patch
 lint-fix:
 	golangci-lint run --fix ./cmd/... ./config/... ./internal/...
+	golangci-lint run --fix --build-tags=e2e ./tests/e2e/...
+	golangci-lint run --fix --build-tags=integration ./tests/integration/...
+	golangci-lint run --fix --build-tags=contract ./tests/contract/...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` at line 93, The lint-fix target currently runs "golangci-lint run
--fix ./cmd/... ./config/... ./internal/..." and misses the same file sets the
lint target checks (so fixes don't cover tagged test trees); update the lint-fix
command to use the exact same path globs as the lint target (replace the current
path list in the "golangci-lint run --fix ..." invocation with the same
arguments used by the lint target) so both targets operate on an identical
scope.

43 changes: 42 additions & 1 deletion internal/admin/dashboard/dashboard.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package dashboard

import (
"bytes"
"crypto/sha256"
"embed"
"encoding/hex"
"html/template"
"io/fs"
"net/http"
"strings"

"github.com/labstack/echo/v5"
)
Expand All @@ -22,7 +25,16 @@ type Handler struct {

// New creates a new dashboard handler with parsed templates and static file server.
func New() (*Handler, error) {
tmpl, err := template.ParseFS(content, "templates/*.html")
assetVersions, err := buildAssetVersions("css/dashboard.css")
if err != nil {
return nil, err
}

tmpl, err := template.New("layout").Funcs(template.FuncMap{
"assetURL": func(path string) string {
return assetURL(path, assetVersions)
},
}).ParseFS(content, "templates/*.html")
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -55,3 +67,32 @@ func (h *Handler) Static(c *echo.Context) error {
h.staticFS.ServeHTTP(c.Response(), c.Request())
return nil
}

func buildAssetVersions(paths ...string) (map[string]string, error) {
versions := make(map[string]string, len(paths))
for _, path := range paths {
normalizedPath := strings.TrimLeft(strings.TrimSpace(path), "/")
if normalizedPath == "" {
continue
}
data, err := content.ReadFile("static/" + normalizedPath)
if err != nil {
return nil, err
}
sum := sha256.Sum256(data)
versions[normalizedPath] = hex.EncodeToString(sum[:6])
}
return versions, nil
}
Comment thread
SantiagoDePolonia marked this conversation as resolved.

func assetURL(path string, versions map[string]string) string {
normalizedPath := strings.TrimLeft(strings.TrimSpace(path), "/")
if normalizedPath == "" {
return "/admin/static/"
}
urlPath := "/admin/static/" + normalizedPath
if version := versions[normalizedPath]; version != "" {
return urlPath + "?v=" + version
}
return urlPath
}
4 changes: 4 additions & 0 deletions internal/admin/dashboard/dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package dashboard
import (
"net/http"
"net/http/httptest"
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -59,6 +60,9 @@ func TestIndex_ReturnsHTML(t *testing.T) {
if strings.Contains(body, `x-init="init()"`) {
t.Errorf("expected dashboard HTML not to call init() explicitly")
}
if !regexp.MustCompile(`/admin/static/css/dashboard\.css\?v=[0-9a-f]+`).MatchString(rec.Body.String()) {
t.Errorf("expected versioned dashboard CSS link in page HTML")
}
}

func TestStatic_ServesCSS(t *testing.T) {
Expand Down
197 changes: 172 additions & 25 deletions internal/admin/dashboard/static/css/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,10 @@ body {
border-color: var(--accent);
}

.models-filter-input {
max-width: 840px;
}

.table-wrapper {
background: var(--bg-surface);
border: 1px solid var(--border);
Expand Down Expand Up @@ -875,6 +879,9 @@ body {

.mono {
font-family: 'SF Mono', Menlo, Consolas, monospace;
}

.font-size-md {
font-size: 13px;
}

Expand Down Expand Up @@ -942,6 +949,21 @@ td.col-price {
border-color: color-mix(in srgb, var(--danger) 50%, var(--border));
}

.table-icon-btn {
width: 32px;
min-width: 32px;
height: 32px;
padding: 0;
gap: 0;
border-radius: 999px;
}

.table-icon-svg {
width: 14px;
height: 14px;
flex-shrink: 0;
}

.model-alias-editor {
background: var(--bg-surface);
border: 1px solid var(--border);
Expand Down Expand Up @@ -1487,27 +1509,44 @@ td.col-price {
gap: 10px;
}

.audit-filter-row .filter-input:first-child {
grid-column: span 4;
.audit-filter-select {
grid-column: span 2;
min-width: 0;
}

.audit-filter-input {
grid-column: span 2;
.audit-filter-row-search .filter-input {
grid-column: 1 / -1;
max-width: none;
}

.audit-filter-select {
.audit-filter-row-controls .audit-filter-select {
grid-column: span 2;
min-width: 0;
}

.audit-filter-row .pagination-btn {
grid-column: span 1;
min-width: 84px;
.audit-filter-row-controls .pagination-btn {
grid-column: 11 / -1;
justify-self: end;
min-width: 108px;
}

.audit-filter-row-selects .audit-filter-select {
grid-column: span 2;
.audit-clear-btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
background: #fff;
border-color: color-mix(in srgb, #fff 80%, var(--border));
color: #111110;
font-weight: 600;
}

.audit-clear-btn:hover:not(:disabled) {
background: #f5f5f5;
}

.audit-clear-btn .table-icon-svg {
width: 12px;
height: 12px;
}

.audit-log-summary {
Expand Down Expand Up @@ -2661,6 +2700,10 @@ body.conversation-drawer-open {
.aliases-editor-header .table-action-btn {
width: 100%;
}
.alias-actions-cell .table-icon-btn,
.aliases-editor-header .table-icon-btn {
width: 36px;
}
Comment on lines +2703 to +2706

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

The mobile override never increases the icon-button height.

This rule only changes width, so .table-icon-btn keeps the base height: 32px and min-width: 32px. The new icon-only controls stay undersized on touch devices even though this block is clearly trying to bump them to 36px.

♻️ Proposed fix
     .alias-actions-cell .table-icon-btn,
     .aliases-editor-header .table-icon-btn {
         width: 36px;
+        min-width: 36px;
+        height: 36px;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.alias-actions-cell .table-icon-btn,
.aliases-editor-header .table-icon-btn {
width: 36px;
}
.alias-actions-cell .table-icon-btn,
.aliases-editor-header .table-icon-btn {
width: 36px;
min-width: 36px;
height: 36px;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/css/dashboard.css` around lines 2703 - 2706,
The mobile override only changes width so the icon buttons keep the base
height/min-width of 32px; update the rule for .alias-actions-cell
.table-icon-btn and .aliases-editor-header .table-icon-btn to also set height:
36px and min-width: 36px (and optionally adjust line-height/alignment if needed)
so the icon-only controls actually grow to the intended touch-friendly size.


/* Audit page mobile */
.audit-log-toolbar {
Expand All @@ -2669,8 +2712,7 @@ body.conversation-drawer-open {
.audit-filter-row {
grid-template-columns: 1fr;
}
.audit-filter-row .filter-input:first-child,
.audit-filter-input,
.audit-filter-row .filter-input,
.audit-filter-select,
.audit-filter-row .pagination-btn {
grid-column: auto;
Expand Down Expand Up @@ -2763,27 +2805,132 @@ body.conversation-drawer-open {
position: absolute;
top: 12px;
right: 14px;
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
justify-content: flex-end;
max-width: calc(100% - 28px);
}

.exec-pipeline-meta-chip {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 999px;
gap: 0;
min-width: 0;
max-width: calc(100% - 28px);
padding: 2px 10px;
border-radius: 12px;
border: 1px solid var(--border);
background: color-mix(in srgb, var(--bg-surface) 86%, transparent);
color: var(--text-muted);
font-size: 10px;
font-size: 12px;
font-weight: 500;
line-height: 1.2;
white-space: nowrap;
}

.exec-pipeline-meta-copy {
appearance: none;
cursor: pointer;
text-align: left;
overflow: hidden;
transition: background-color 0.15s, border-color 0.15s, color 0.15s, box-shadow 0.15s;
}

.exec-pipeline-meta-copy:hover,
.exec-pipeline-meta-copy:focus-visible {
border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
background: color-mix(in srgb, var(--accent) 8%, var(--bg-surface));
color: color-mix(in srgb, var(--accent) 74%, var(--text));
}

.exec-pipeline-meta-copy:focus-visible {
outline: none;
box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);
}

.exec-pipeline-meta-label {
flex: 0 0 auto;
font-weight: 700;
}

.exec-pipeline-meta-placeholder {
flex: 0 0 auto;
max-width: 3ch;
margin-left: 4px;
overflow: hidden;
opacity: 1;
transition: max-width 0.18s ease, margin-left 0.18s ease, opacity 0.15s ease;
}

.exec-pipeline-meta-value {
flex: 0 1 auto;
max-width: 0;
margin-left: 0;
overflow: hidden;
opacity: 0;
text-overflow: clip;
transition: max-width 0.22s ease, margin-left 0.18s ease, opacity 0.15s ease;
}

.exec-pipeline-meta-copy:hover .exec-pipeline-meta-placeholder,
.exec-pipeline-meta-copy:focus-visible .exec-pipeline-meta-placeholder,
.exec-pipeline-meta-copied .exec-pipeline-meta-placeholder,
.exec-pipeline-meta-error .exec-pipeline-meta-placeholder {
max-width: 0;
margin-left: 0;
opacity: 0;
}

.exec-pipeline-meta-copy:hover .exec-pipeline-meta-value,
.exec-pipeline-meta-copy:focus-visible .exec-pipeline-meta-value,
.exec-pipeline-meta-copied .exec-pipeline-meta-value,
.exec-pipeline-meta-error .exec-pipeline-meta-value {
max-width: 42ch;
margin-left: 4px;
opacity: 1;
}

.exec-pipeline-meta-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 0;
height: 14px;
margin-left: 0;
overflow: hidden;
opacity: 0;
line-height: 0;
transform: translateX(4px) translateY(1px) scale(0.84);
transition: width 0.18s ease, margin-left 0.18s ease, opacity 0.15s ease, transform 0.18s ease;
}

.exec-pipeline-meta-icon svg {
width: 14px;
height: 14px;
stroke: currentcolor;
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
}

.exec-pipeline-meta-copied,
.exec-pipeline-meta-copied:hover,
.exec-pipeline-meta-copied:focus-visible {
background: color-mix(in srgb, var(--success) 12%, var(--bg));
border-color: color-mix(in srgb, var(--success) 40%, var(--border));
color: var(--success);
}

.exec-pipeline-meta-copied .exec-pipeline-meta-icon {
width: 14px;
margin-left: 6px;
opacity: 1;
transform: translateY(1px);
}

.exec-pipeline-meta-error,
.exec-pipeline-meta-error:hover,
.exec-pipeline-meta-error:focus-visible {
background: color-mix(in srgb, var(--danger) 10%, var(--bg));
border-color: color-mix(in srgb, var(--danger) 34%, var(--border));
color: var(--danger);
}

/* ─── Main pipeline row ─── */

.exec-pipeline-row {
Expand Down
Loading
Loading