sbool assisting; static S generatorsID; static JFrame frame; static JTable table, chatTable; //static JTextArea chat; static JTextField input; static L log = synchroList(); static L recommendations; static L thinkThreads = synchroList(); static JLabel status; static S lastInput; static Map matchingSuggestion; static Map> allLogs = synchroMap(); static JFrame analyzersFrame; static S dialogImageID; static JFrame dialogImageFrame; static int listDelay = 2000; static int maxLineLength = 1000; static int maxLongStringLength = 100*1000; static Bool thinking; static new Thinker thinker; static bool showCPU = true; static S systemPrefix = "[system]"; static S dialog = "new"; svoid randomMain { //substanceLAF("EmeraldDusk"); // Too dark! //substanceLAF("ChallengerDeep"); // So purple! //substance("MistAqua"); substance("Moderate"); table = tableWithTooltips(); //chat = autoScroll(wordWrapTextArea()); chatTable = tableWithTooltips(); input = new JTextField; status = new JLabel(" "); S title = assisting ? "Assistance - " + autoFrameTitle() : autoFrameTitle(); frame = showFrame(title, vgrid(centerAndSouth( //jtabs(1, "Chat", chat, "Details", chatTable), chatTable, input), centerAndSouth(table, status))); //setFrameIconLater(frame, "#1003593"); addMenu(frame, "Random", "Delete last line (!delete)", r { post("!delete"); }, "Reload generators (!gen)", r { post("!gen"); }, "Restart app (!restart)", r { post("!restart"); }, "Restart Java engine (!fresh)", r { post("!fresh"); }, "Execute Java code (!j ...)", r { setInput("!j 1+2"); }, "Switch dialog (!dialog ...)", r { setInput("!dialog bla"); }, "Restore last input", r { if (nempty(lastInput)) setInput(lastInput); }, "Show raw dialog", r { showText("Raw Dialog", rawDialog()); }, ); makeDialogsMenu(); onEnter(input, r { post(); }); onDoubleClick(table, voidfunc(int row) { chooseSuggestion(row); }); for (int i = 1; i <= 12; i++) { final int _i = i; registerFunctionKey(frame, i, r { chooseSuggestionForEditing(_i-1); }); registerShiftFunctionKey(frame, i, r { chooseSuggestion(_i-1); }); registerCtrlFunctionKey(frame, i, r { // post user input and then choose suggestion for editing Map map = getTableLineAsMap(table, _i-1); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); logEvent("Suggestion chosen for editing with pre-post", mapPlus(map, "Row", _i, "Input", getInput(), "Top Suggesters", topSuggesters())); post(); setInput(s); }); } onDoubleClickOrEnter(chatTable, voidfunc(int row) { L line = getTableLine(chatTable, row); if (line != null) setInput(getString(line, 0)); }); onUpdate(input, r { updateOnce(); }); loadDialog(); logEvent("Starting"); updateOnce(); input.requestFocus(); if (isAction(last(log)) && confirmYesNo(input, "Run action? " + last(log))) action(last(log)); } static S getInput() { ret joinLines(" # ", input.getText().trim()); } sv post() { postAsUser(getInput(), null); } static void postAsUser(S i, Map infos) { if (inputAllowedByUser(i)) post(i, infos); } svoid chooseSuggestionForEditing(int row) { Map map = getTableLineAsMap(table, row); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); logEvent("Suggestion chosen for editing", mapPlus(map, "Row", row+1, "Input", getInput(), "Top Suggesters", topSuggesters())); setInput(s); } svoid setInput(final S s) { awtIfNecessary { lastInput = input.getText(); input.setText(s); input.selectAll(); input.requestFocus(); } } svoid rankToTop(Map map) { rankToTop(map, false); } svoid rankToTop(Map map, bool removeISuggesters) { if (map == null) ret; O s = map.get("Suggesters"); // Table cells have been structure'd by dataToTable L sugg = s instanceof L ? (L) s : (L) unstructure((S) s); // These are cheaters! if (removeISuggesters) sugg = rejectWhere(func(S s) { s.endsWith("/i") }, sugg); thinker.rankToTop(first(sugg)); } svoid chooseSuggestion(int row) { Map map = getTableLineAsMap(table, row); if (map == null) ret; rankToTop(map); S s = trim(unnull(map.get("Suggestion"))); if (empty(s)) ret; //logEvent("Suggestion chosen", mapPlus(map, "Row", row+1, "Input", getInput(), "Top Suggesters", topSuggesters)); setInput(s); postAsUser(s, mapPlus(map, "Index", row+1)); } static L topSuggesters() { int n = 20; n = min(n, tableRows(table)); new L topSuggesters; for (int i = 0; i < n; i++) topSuggesters.add(getTableLineAsMap(table, i)); //if (empty(topSuggesters)) topSuggesters = null; ret topSuggesters; } svoid logEvent(S type) { logEvent(type, litmap()); } svoid logEvent(S type, Map map) { logStructure(new File(dialogDir(), "event.log"), ll(type, chatTime(), map)); } static bool inputAllowedByUser(S i) { ret !swic(i, systemPrefix); } // may be called from other thread static void postSystemMessage(final S msg) { if (empty(msg)) ret; awtIfNecessary { post(systemPrefix + " " + msg, litmap("By", "System")); } } static void post(S i) { post(i, null); } static void post(S i, Map infos) { try { i = trim(i); if (empty(i)) ret; //i = escapeNewLines(i); if (infos == null) { infos = matchingSuggestion; if (infos != null) print("Ranking to top: " + struct(infos)); rankToTop(infos, true); } infos = mapPlus(infos, "Top Suggesters", topSuggesters()); bool tooLong = l(i) > maxLongStringLength; if (l(i) > maxLineLength) { S id = saveLongString(i); i = substring(i, 0, maxLineLength) + "... [" + (tooLong ? "too " : "") + "long text " + id + "]"; } } catch e { printStackTrace(e); i = systemPrefix + " " + exceptionToStringShort(e); } S s = i + "\n"; //chat.append(escapeNewLines(i) + "\n"); appendToFile(logFile(), "[" + chatTime() + "] " + s); logEvent("Posting", litmap("Text", i, "Infos", infos)); log.add(i); updateChatTable(); input.selectAll(); updateOnce(); try { action(i); } catch e { printStackTrace(e); postSystemMessage(exceptionToStringShort(e)); } } static S dropActionPrefix(S s) { if (s == null) null; s = dropBracketPrefix(s); // e.g. "[bot]" if (!s.startsWith("!")) null; ret s.substring(1); } static bool isAction(S s) { ret dropActionPrefix(s) != null; } static void action(S s) { s = dropActionPrefix(s); if (s == null) ret; final S _s = s; thread "Action" { JWindow _loading_window = showLoadingAnimation(); try { genLog_set(getLog()); // 'case user needs it gOtherLogs_set(getOtherLogs()); // ditto randomsOwnCmds(_s); systemCommands(_s, mc()); } catch e { printStackTrace(e); postSystemMessage("Error - " + exceptionToStringShort(e)); } finally { genLog_clear(); gOtherLogs_clear(); disposeWindow(_loading_window); } } } static volatile bool again; // This logic is bad... static void fillList(bool force) { bool t = force || shouldUpdateList(); if (neq(t, thinking)) { thinking = t; setFrameIcon(table, t ? "#1003603" : "#1003593"); } if (!t) { if (!force) againl8r(); } else { if (nempty(thinkThreads)) { again = true; ret; } fillListImpl(); } } static void addKeys(L data) { for (int i = 0; i < l(data); i++) { int k = i+1; S key = k <= 12 ? "F" + k : null; Map m = litorderedmap("Key", key); m.putAll(data.get(i)); data.set(i, m); } } static void removeMatchingLine(L data) { S input = getInput(); matchingSuggestion = null; for (int i = 0; i < l(data); i++) if (eq(input, data.get(i).get("Suggestion"))) { matchingSuggestion = mapPlus(data.get(i), "Index", i+1); data.remove(i); ret; } } static void fillListImpl() { thread "Fill List" { try { thinkThreads.add(currentThread()); final new L data; thinker.makeListData(cloneList(log), getInput(), getOtherLogs(), data); awt { pcall { removeMatchingLine(data); addKeys(data); dataToTable_uneditable(table, data); tableColumnMaxWidth(table, 0, 30); // "Key" column } againl8r(); } } finally { thinkThreads.remove(currentThread()); if (again) { again = false; fillListImpl(); } } } } static void updateOnce() { fillList(true); } static void againl8r() { swingAfter(table, listDelay, r { fillList(false); }); } static bool shouldUpdateList() { bool result = false; S text = " "; if (getFrame(table).isFocused()) { result = !mouseInComponent(table); text = result ? " Thinking..." + (showCPU /*&& thinker.load != 0*/ ? " (" + thinker.load + "% CPU)" : "") : "Not thinking cause you got the mouse in there"; } status.setText(text); ret result; } // also called from outside static L loadLog() { log.clear(); log.addAll(scanEventLogForText(dialogDir())); ret log; } static void loadAllLogs() { allLogs.clear(); for (File f : findAllFiles(getProgramDir())) if (f.getName().equals("event.log")) allLogs.put(f.getParentFile().getName(), new ImmL(scanEventLogForText(f))); } synchronized static L getLastFromLog(int n) { ret cloneList(getLast(log, n)); } synchronized static L getLog() { ret cloneList(log); } static File dialogDir() { ret prepareProgramFile(dialog); } static File logFile() { ret new File(dialogDir(), "log.txt"); } static void switchDialog(final S name) { swingAndWait(r { dialog = name; touchFile(new File(dialogDir(), "event.log")); loadDialog(); loadAllLogs(); makeDialogsMenu(); setFrameTitle(dialog + " - " + getProgramTitle()); // show special stuff for dialog, like an image dialogImageID = trim(readTextFile(new File(dialogDir(), "image-id.txt"))); showDialogImage(); }); } static void loadDialog() { loadLog(); thinker = new Thinker; thinker.startUp(log); //chat.setText(joinLines(log)); updateChatTable(); } static void randomsOwnCmds(S s) { new Matches m; if "dialog *" { switchDialog(m.unq(0)); // TODO: show current dialog somewhere else //postSystemMessage("OK, dialog switched to " + quote(dialog)); } if "gen" generators = null; if "delete" updateChatTable(); if "analyzers" showAnalyzers(); if (matchOneOf(s, m, "img *", "image *")) { S imageID = m.fsi(0); saveTextFile(new File(dialogDir(), "image-id.txt"), imageID); dialogImageID = imageID; showDialogImage(); } if (matchOneOf(s, m, "img", "image")) { if (empty(dialogImageID)) postSystemMessage("No image set for this dialog"); else postSystemMessage("Image for this dialog: " + fsI(dialogImageID)); } } static void showAnalyzers() { if (analyzersFrame == null) analyzersFrame = showFrame("Analyzers"); } static void updateChatTable() { awtIfNecessary { new L data; L l = scanLog_safeUnstructure(new File(dialogDir(), "event.log")); for (int i = 0; i < l(l); i++) pcall { L a = l.get(i), prev = get(l, i-1); if (firstIs(a, "Posting")) { Map map = cast get(a, 2); S text = getString(map, "Text").trim(); if (eq(text, "!delete")) { removeLast(data); continue; } S idx = ""; Map infos = cast map.get("Infos"); if (infos != null && infos.containsKey("Index")) idx = str(infos.get("Index")); else pcall { //printStruct("prev: ", prev); if (prev != null && firstIs(prev, "Suggestion chosen for editing")) { Map m = getMap(prev, 2); S suggestion = getString(m, "Suggestion"); //print("Suggestion: " + structure(suggestion)); idx = str(get(m, "Row")); if (neq(suggestion, text)) idx += "?"; } } data.add(litorderedmap("Text", escapeNewLines(text), "Sugg." /* Suggestion Index */, idx)); } } dataToTable_uneditable(chatTable, data); tableColumnMaxWidth(chatTable, 1, 40); // enough for 2 digits and a "?" scrollTableDownIn(chatTable, 50); } } static synchronized S saveLongString(S s) { s = substring(s, 0, maxLongStringLength); S id; File f; do { id = randomID(10); f = getProgramFile("long-strings/" + id); } while (f.exists()); saveTextFile(f, s); ret id; } static O generators; static void makeGenerators(L l) { synchronized(main.class) { if (!isSnippetID(generatorsID)) fail("No generators ID set"); if (generators == null) generators = hotwire(generatorsID); } new L l2; callOpt(generators, "makeGenerators", l2); callOpt(generators, "deterministicGenerators", l2); l.addAll((L) quickImport(l2)); } static S rawDialog() { ret fromLines(log); } static void makeDialogsMenu() { new L items; for (File dir : listDirs(getProgramDir())) if (containsFile(dir, "event.log")) { fS dialog = dir.getName(); items.add(dialog); items.add(r { switchDialog(dialog); }); } addMenu(frame, "Dialogs", items); } static L> getOtherLogs() { // TODO: sort by latest or smth? ret valuesList(mapMinus(allLogs, dialog)); } static void hideDialogImage() { if (dialogImageFrame != null) { disposeFrame(dialogImageFrame); dialogImageFrame = null; } } static void showDialogImage() { hideDialogImage(); if (nempty(dialogImageID)) { S title = getSnippetTitle(dialogImageID) + " [" + fsI(dialogImageID) + "/" + dialog + "]"; dialogImageFrame = getFrame(showImage(dialogImageID, title)); } }