diff --git a/network-poc/static/index.html b/network-poc/static/index.html
index 69ceef1..213023a 100644
--- a/network-poc/static/index.html
+++ b/network-poc/static/index.html
@@ -1915,8 +1915,78 @@
termLog(` kpn: tuntematon alikomento "${sub}". Kokeile: kpn help`, '#f85149');
}
+ // Tab-completion: ennustava komennonsyöttö sana kerrallaan
+ const kpnCommands = {
+ 'kpn': ['help', 'run', 'pipeline', 'load', 'status', 'models', 'hello', 'clear'],
+ 'kpn run': ['coder', 'manager', 'tester', 'qa', 'data', 'observer', 'qwen-coder', 'smollm-135m', 'qwen-05b', 'phi3-mini'],
+ 'kpn pipeline': ['"'],
+ };
+ // Esimerkkipromptit malleittain
+ const kpnExamples = {
+ 'kpn run coder': ['"hello world in python"', '"fibonacci in rust"', '"quicksort in javascript"'],
+ 'kpn run manager': ['"suunnittele REST API"', '"priorisoi tiimin tehtävät"'],
+ 'kpn run tester': ['"testaa login-toiminto"'],
+ 'kpn pipeline': ['"rakenna todo-sovellus"', '"tee laskin pythonilla"'],
+ };
+
+ function tabComplete(input) {
+ const val = input.value;
+ const words = val.trimEnd().split(/\s+/);
+
+ // Etsitään sopiva täydennystaso
+ // "kpn" → "kpn " alikomennot, "kpn run" → mallit, "kpn run coder" → prompti
+ for (let depth = words.length; depth >= 1; depth--) {
+ const prefix = words.slice(0, depth).join(' ');
+ const partial = words[depth] || '';
+
+ // Tarkistetaan esimerkkipromptit ensin
+ if (kpnExamples[prefix] && !partial) {
+ const example = kpnExamples[prefix][Math.floor(Math.random() * kpnExamples[prefix].length)];
+ input.value = prefix + ' ' + example;
+ return true;
+ }
+
+ // Komentojen täydennys
+ const candidates = kpnCommands[prefix];
+ if (candidates) {
+ const matches = partial
+ ? candidates.filter(c => c.startsWith(partial))
+ : candidates;
+ if (matches.length === 1) {
+ words[depth] = matches[0];
+ input.value = words.slice(0, depth + 1).join(' ') + ' ';
+ return true;
+ } else if (matches.length > 1 && !partial) {
+ input.value = prefix + ' ' + matches[0];
+ return true;
+ } else if (matches.length > 1) {
+ // Yhteinen etuliite
+ let common = matches[0];
+ for (const m of matches) {
+ while (!m.startsWith(common)) common = common.slice(0, -1);
+ }
+ if (common.length > partial.length) {
+ words[depth] = common;
+ input.value = words.slice(0, depth + 1).join(' ');
+ return true;
+ }
+ }
+ }
+ }
+
+ // Tyhjä input → "kpn "
+ if (!val.trim()) {
+ input.value = 'kpn ';
+ return true;
+ }
+ return false;
+ }
+
termInput?.addEventListener('keydown', (e) => {
- if (e.key === 'Enter') {
+ if (e.key === 'Tab') {
+ e.preventDefault();
+ tabComplete(termInput);
+ } else if (e.key === 'Enter') {
const cmd = termInput.value.trim();
if (cmd) termExec(cmd);
termInput.value = '';