diff --git a/dist/hpl-toolbox-0.1.6.exe b/dist/hpl-toolbox-0.1.6.exe
index 47293ee..ce7cd40 100644
Binary files a/dist/hpl-toolbox-0.1.6.exe and b/dist/hpl-toolbox-0.1.6.exe differ
diff --git a/dist/hpl-toolbox-0.1.6.vsix b/dist/hpl-toolbox-0.1.6.vsix
index 417596c..0a579f6 100644
Binary files a/dist/hpl-toolbox-0.1.6.vsix and b/dist/hpl-toolbox-0.1.6.vsix differ
diff --git a/src/launcherView.ts b/src/launcherView.ts
index 66fd6e1..bb77429 100644
--- a/src/launcherView.ts
+++ b/src/launcherView.ts
@@ -65,7 +65,6 @@ export class LauncherViewProvider implements vscode.WebviewViewProvider {
function getHtml(version: string, betaToolsEnabled: boolean): string {
const visibleTools = sortToolsForDisplay(TOOLS.filter(tool => betaToolsEnabled || !tool.beta));
- const hiddenBetaCount = TOOLS.filter(tool => tool.beta).length - visibleTools.filter(tool => tool.beta).length;
const toolButtons = visibleTools.map(renderToolButton).join('\n');
return `
@@ -140,38 +139,35 @@ function getHtml(version: string, betaToolsEnabled: boolean): string {
font-size: 10px;
text-transform: uppercase;
}
- .beta-toggle {
- margin-top: 12px;
+ .footer {
+ margin-top: auto;
padding-top: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ color: var(--vscode-descriptionForeground);
+ font-size: 8px;
border-top: 1px solid var(--vscode-panel-border);
}
- .beta-toggle button {
- width: 100%;
- padding: 7px 9px;
+ .beta-toggle {
+ padding: 0;
background: transparent;
- color: var(--vscode-foreground);
- border: 1px solid var(--vscode-panel-border);
- border-radius: 5px;
+ color: var(--vscode-descriptionForeground);
+ border: 0;
cursor: pointer;
text-align: left;
font: inherit;
- }
- .beta-toggle button:hover {
- background: var(--vscode-list-hoverBackground, var(--vscode-button-secondaryHoverBackground));
- }
- .beta-note {
- display: block;
- margin-top: 3px;
- color: var(--vscode-descriptionForeground);
font-size: 10px;
- line-height: 1.3;
+ opacity: 0.72;
}
- .footer {
- margin-top: 8px;
- color: var(--vscode-descriptionForeground);
- font-size: 8px;
- text-align: right;
+ .beta-toggle:hover {
+ opacity: 1;
+ text-decoration: underline;
+ }
+ .footer-version {
opacity: 0.75;
+ text-align: right;
}
@@ -179,13 +175,10 @@ function getHtml(version: string, betaToolsEnabled: boolean): string {
-
+
-
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- _, _ = w.Write([]byte(Page("/applovin", "AppLovin Upload", body)))
+ _, _ = w.Write([]byte(Page("/applovin", "AppLovin Playable Preview", body)))
}
func ApplovinPick(w http.ResponseWriter, r *http.Request) {
@@ -253,6 +323,23 @@ func ApplovinPick(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"files": files})
}
+func ApplovinPickFolder(w http.ResponseWriter, r *http.Request) {
+ cfg := LoadConfig()
+ dir := PickFolder("Select folder", cfg.LastPickDir)
+ if dir == "" {
+ writeJSON(w, map[string]any{"files": []any{}})
+ return
+ }
+ cfg.LastPickDir = dir
+ _ = SaveConfig(cfg)
+ paths := collectHTMLFiles(dir)
+ files := make([]map[string]string, 0, len(paths))
+ for _, p := range paths {
+ files = append(files, map[string]string{"path": p, "name": filepath.Base(p)})
+ }
+ writeJSON(w, map[string]any{"files": files})
+}
+
func ApplovinUpload(w http.ResponseWriter, r *http.Request) {
var req struct {
Paths []string `json:"paths"`
diff --git a/standalone/base64.go b/standalone/base64.go
index 951fd97..465dee3 100644
--- a/standalone/base64.go
+++ b/standalone/base64.go
@@ -12,83 +12,119 @@ import (
func Base64Page(w http.ResponseWriter, r *http.Request) {
body := `
-
Base64 Asset Scanner
-
-
-
-
-
-
-
-
-
+
+
+
+
@@ -99,7 +135,11 @@ document.getElementById('scan').addEventListener('click', async () => {
func Base64PickFolder(w http.ResponseWriter, r *http.Request) {
p := PickFolder("Select folder to scan", "")
- writeJSON(w, map[string]any{"path": p})
+ if p == "" {
+ writeJSON(w, map[string]any{"paths": []string{}})
+ return
+ }
+ writeJSON(w, map[string]any{"paths": collectHTMLFiles(p)})
}
func Base64PickFiles(w http.ResponseWriter, r *http.Request) {
diff --git a/standalone/home.go b/standalone/home.go
index 26f475d..a85793d 100644
--- a/standalone/home.go
+++ b/standalone/home.go
@@ -12,19 +12,37 @@ func HomePage(w http.ResponseWriter, r *http.Request) {
if n.Path == "/" {
continue
}
- label := n.Label
+ badge := ""
if n.Beta {
- label += `
Beta`
+ badge = `
Beta`
}
- items.WriteString(`
` + label + ` — ` + n.Description + ``)
+ items.WriteString(`
` + n.Label + badge + `` + n.Description + ``)
}
body := `
-
HPL Toolbox
-
Standalone build. Pick a tool from the top bar.
-
+
+
` + items.String() + `
-
+
+
`
w.Header().Set("Content-Type", "text/html; charset=utf-8")
_, _ = w.Write([]byte(Page("/", "Home", body)))
diff --git a/standalone/layout.go b/standalone/layout.go
index 290214b..02f92d0 100644
--- a/standalone/layout.go
+++ b/standalone/layout.go
@@ -3,13 +3,25 @@ package main
import (
"encoding/json"
"net/http"
- "strconv"
"strings"
)
const SharedCSS = `
- :root { color-scheme: dark; }
+ :root {
+ color-scheme: dark;
+ --tool-gap-sm: 8px;
+ --tool-gap-md: 12px;
+ --tool-gap-lg: 16px;
+ --tool-radius: 5px;
+ --tool-border: #333;
+ --tool-panel: #252526;
+ }
+ * { box-sizing: border-box; }
+ html { min-height: 100%; }
body {
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
background: #1e1e1e; color: #ddd;
margin: 0; padding: 0;
@@ -19,17 +31,49 @@ const SharedCSS = `
padding: 8px 16px; background: #252526; border-bottom: 1px solid #333;
font-size: 13px;
}
- .topbar a { color: #9cdcfe; text-decoration: none; padding: 4px 10px; border-radius: 3px; }
+ .topbar a { color: #ddd; text-decoration: none; padding: 5px 10px; border-radius: 4px; }
.topbar a:hover { background: #2a2d2e; }
.topbar a.active { background: #094771; color: #fff; }
.beta-pill { margin-left: 4px; padding: 1px 4px; border: 1px solid #555; border-radius: 3px; font-size: 10px; opacity: 0.7; text-transform: uppercase; }
- .topbar-version { margin-left: auto; font-size: 11px; opacity: 0.45; letter-spacing: 0.4px; }
- .content { padding: 16px; max-width: 980px; margin: 0 auto; }
- .beta-tools-footer { max-width: 980px; margin: 24px auto 16px auto; padding: 12px 16px 0; border-top: 1px solid #333; }
- .beta-tools-footer button { width: 100%; text-align: left; background: #3a3d41; color: #ddd; }
- .beta-tools-footer button:hover { background: #45494e; }
- .beta-note { display: block; margin-top: 3px; opacity: 0.65; font-size: 11px; }
- h2 { margin-top: 0; font-weight: 500; }
+ .topbar-spacer { margin-left: auto; }
+ .content { flex: 1; width: 100%; padding: 18px; max-width: 1100px; margin: 0 auto; }
+ .app-footer {
+ width: 100%;
+ max-width: 1100px;
+ margin: auto auto 0;
+ padding: 8px 18px 14px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ color: #a7a7a7;
+ font-size: 10px;
+ border-top: 1px solid #333;
+ }
+ .beta-tools-toggle {
+ padding: 0;
+ background: transparent;
+ color: #a7a7a7;
+ border: 0;
+ cursor: pointer;
+ font: inherit;
+ opacity: 0.72;
+ }
+ .beta-tools-toggle:hover { background: transparent; opacity: 1; text-decoration: underline; }
+ .app-footer-version { opacity: 0.75; text-align: right; }
+ h2 { margin: 0; font-size: 18px; line-height: 1.25; font-weight: 600; }
+ .tool-header { margin-bottom: var(--tool-gap-lg); }
+ .tool-title { margin: 0; font-size: 18px; line-height: 1.25; font-weight: 600; }
+ .tool-description, .section-description, .muted { color: #a7a7a7; }
+ .tool-description { margin: 5px 0 0; max-width: 780px; font-size: 12px; line-height: 1.45; }
+ .tool-panel { margin-bottom: var(--tool-gap-lg); border: 1px solid var(--tool-border); border-radius: var(--tool-radius); background: rgba(128,128,128,0.08); overflow: hidden; }
+ .panel-header { padding: 10px 12px; border-bottom: 1px solid var(--tool-border); background: var(--tool-panel); }
+ .panel-title { margin: 0; color: #ddd; font-size: 12px; font-weight: 700; letter-spacing: 0.45px; text-transform: uppercase; }
+ .panel-body { padding: 12px; }
+ .control-group + .control-group { margin-top: var(--tool-gap-md); }
+ .control-label { margin: 0 0 6px; color: #a7a7a7; font-size: 11px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; }
+ .file-row, .field-row, .action-row { display: flex; align-items: center; gap: var(--tool-gap-sm); flex-wrap: wrap; }
+ .action-row { margin-top: var(--tool-gap-md); }
input[type=text], input[type=date], select, textarea {
box-sizing: border-box; padding: 6px 8px;
background: #3c3c3c; color: #ddd;
@@ -46,7 +90,30 @@ const SharedCSS = `
button:hover { background: #1177bb; }
button.secondary { background: #3a3d41; color: #ddd; }
button.secondary:hover { background: #45494e; }
+ button.danger { background: transparent; color: #f48771; border: 1px solid #444; }
+ button.danger:hover { background: rgba(244,135,113,0.14); }
button:disabled { opacity: 0.5; cursor: not-allowed; }
+ .status-panel, .results-panel, .result-card { border: 1px solid var(--tool-border); border-radius: var(--tool-radius); background: rgba(128,128,128,0.08); }
+ .status-panel { min-height: 30px; margin-top: var(--tool-gap-md); padding: 8px 10px; white-space: pre-wrap; }
+ .status-panel:empty { display: none; }
+ .results-panel { margin-top: var(--tool-gap-md); overflow: auto; }
+ .results-panel:empty { display: none; }
+ .result-card { padding: 10px; word-break: break-word; }
+ .data-table { width: 100%; border-collapse: collapse; table-layout: fixed; }
+ .data-table th, .data-table td { padding: 8px 10px; text-align: left; vertical-align: top; border-bottom: 1px solid var(--tool-border); }
+ .data-table th { color: #a7a7a7; font-size: 11px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; background: var(--tool-panel); }
+ .data-table tr:last-child td { border-bottom: none; }
+ .mono { font-family: Consolas, "Courier New", monospace; font-size: 12px; }
+ .wrap { overflow-wrap: anywhere; word-break: break-word; }
+ .file-name { color: #a7a7a7; font-size: 12px; overflow-wrap: anywhere; }
+ .selected-list, .selected-files { margin-top: 8px; }
+ .remove-selected, .remove-btn { min-width: 28px; width: 28px; padding: 3px 0; color: #f48771; font-size: 16px; line-height: 1; }
+ .pager { margin-top: 8px; display: flex; align-items: center; gap: 8px; justify-content: flex-end; }
+ .badge { display: inline-flex; align-items: center; min-height: 18px; padding: 1px 6px; border-radius: 999px; border: 1px solid #444; font-size: 11px; line-height: 1.2; white-space: nowrap; }
+ .badge.ok { color: #89d185; background: rgba(137,209,133,0.12); }
+ .badge.err { color: #f48771; background: rgba(244,135,113,0.14); }
+ .badge.warn { color: #ffd580; background: rgba(210,153,34,0.16); }
+ .is-hidden { display: none !important; }
.err { color: #f48771; white-space: pre-wrap; }
.ok { color: #89d185; }
.hint { font-size: 12px; opacity: 0.7; }
@@ -62,7 +129,7 @@ type navItem struct {
var navItems = []navItem{
{Path: "/", Label: "Home"},
{Path: "/plec", Label: "PLEC Upload", Description: "Upload HTML to the internal PLEC server"},
- {Path: "/applovin", Label: "AppLovin Upload", Description: "Upload to p.applov.in (QR preview)"},
+ {Path: "/applovin", Label: "AppLovin Playable Preview", Description: "Upload to p.applov.in (QR preview)"},
{Path: "/base64", Label: "Base64 Scanner", Description: "Find non-base64 assets in HTML"},
{Path: "/mraid", Label: "MRAID Checker", Description: "Check MRAID requirements and best practices", Beta: true},
{Path: "/mobile", Label: "Send To Mobile", Description: "Share HTML to a phone via LAN + QR"},
@@ -118,11 +185,9 @@ func Page(activePath, title, body string) string {
tabs.WriteString(`` + label + ``)
}
- betaButtonLabel := "Enable Beta Tools"
- betaButtonNote := "Hidden beta tools: " + strconv.Itoa(betaToolCount())
+ betaButtonLabel := "Show beta"
if betaToolsEnabled {
- betaButtonLabel = "Disable Beta Tools"
- betaButtonNote = "Beta tools are visible in this standalone launcher."
+ betaButtonLabel = "Hide beta"
}
return `
@@ -131,10 +196,11 @@ func Page(activePath, title, body string) string {
` + title + ` — HPL Toolbox
-` + tabs.String() + `v` + AppVersion + `
+` + tabs.String() + `
` + body + `
-