File size: 3,809 Bytes
8d1819a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import { createStore } from "/js/AlpineStore.js";

const model = {
  // State
  isLoading: false,
  contextData: null,
  tokenCount: 0,
  error: null,
  editor: null,
  closePromise: null,

  // Open Context Window modal
  async open() {
    if (this.isLoading) return; // Prevent double-open
    
    this.isLoading = true;
    this.error = null;
    this.contextData = null;
    this.tokenCount = 0;

    try {
      // Open modal FIRST (immediate UI feedback, but DON'T await)
      this.closePromise = window.openModal('modals/context/context.html');
      
      // Setup cleanup on modal close
      if (this.closePromise && typeof this.closePromise.then === 'function') {
        this.closePromise.then(() => {
          this.destroy();
        });
      }
      
      this.updateModalTitle(); // Set initial "loading" title
      
      // Fetch data from backend
      const contextId = window.getContext();
      const response = await window.sendJsonData('/ctx_window_get', {
        context: contextId,
      });
      
      // Update state with data
      this.contextData = response.content;
      this.tokenCount = response.tokens || 0;
      this.isLoading = false;
      this.updateModalTitle(); // Update with token count
      
      // Initialize ACE editor
      this.scheduleEditorInit();
      
    } catch (error) {
      console.error("Context fetch error:", error);
      this.error = error?.message || "Failed to load context window";
      this.isLoading = false;
      this.updateModalTitle(); // Show error in title
    }
  },

  scheduleEditorInit() {
    // Use double requestAnimationFrame to ensure DOM is ready
    window.requestAnimationFrame(() => {
      if (this.isLoading || this.error) return;
      window.requestAnimationFrame(() => this.initEditor());
    });
  },

  initEditor() {
    const container = document.getElementById("context-viewer-container");
    if (!container) {
      console.warn("Context container not found, deferring editor init");
      return;
    }

    // Destroy old instance if exists
    if (this.editor?.destroy) {
      this.editor.destroy();
    }

    // Check if ACE is available
    if (!window.ace?.edit) {
      console.error("ACE editor not available");
      this.error = "Editor library not loaded";
      return;
    }

    const editorInstance = window.ace.edit("context-viewer-container");
    if (!editorInstance) {
      console.error("Failed to create ACE editor instance");
      return;
    }

    this.editor = editorInstance;

    // Configure theme based on dark mode (legacy parity: != "false")
    const darkMode = window.localStorage?.getItem("darkMode");
    const theme = darkMode !== "false" ? "ace/theme/github_dark" : "ace/theme/tomorrow";

    this.editor.setTheme(theme);
    this.editor.session.setMode("ace/mode/markdown");
    this.editor.setValue(this.contextData, -1); // -1 moves cursor to start
    this.editor.setReadOnly(true);
    this.editor.clearSelection();
  },

  updateModalTitle() {
    window.requestAnimationFrame(() => {
      const modalTitles = document.querySelectorAll(".modal.show .modal-title");
      if (!modalTitles.length) return;
      
      // Get the last (topmost) modal title
      const title = modalTitles[modalTitles.length - 1];
      if (!title) return;

      if (this.error) {
        title.textContent = "Context Window – Error";
      } else if (this.isLoading) {
        title.textContent = "Context Window (loading…)";
      } else {
        title.textContent = `Context Window ~${this.tokenCount} tokens`;
      }
    });
  },

  // Optional: cleanup method for lifecycle management
  destroy() {
    if (this.editor?.destroy) {
      this.editor.destroy();
    }
    this.editor = null;
  },
};

export const store = createStore("context", model);