File size: 7,602 Bytes
63dfab5
ebc1b3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5a083bc
 
 
 
 
 
 
 
 
 
 
ebc1b3a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63dfab5
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<!doctype html>
<html lang="zh-Hant">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>WFreeCAD · tiny part studio</title>
  <meta name="description" content="A tiny in-browser FreeCAD Python playground. Load a Part macro and orbit the result.">
  <link rel="icon" href="favicon.svg" type="image/svg+xml">
  <link rel="stylesheet" href="vendor/codemirror/codemirror.css">
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <header class="topbar">
    <a class="brand" href="./" aria-label="WFreeCAD 首頁">
      <span class="brand-mark" aria-hidden="true">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round">
          <circle cx="12" cy="12" r="3.1"/>
          <path d="M12 3v2.6M12 18.4V21M3 12h2.6M18.4 12H21M5.6 5.6l1.9 1.9M16.5 16.5l1.9 1.9M18.4 5.6l-1.9 1.9M7.5 16.5l-1.9 1.9"/>
        </svg>
      </span>
      <span class="brand-name">WFreeCAD</span>
      <span class="brand-sub">part studio</span>
    </a>

    <div class="topbar-actions">
      <div class="runtime" id="runtime" data-state="idle" title="Python 執行環境">
        <span class="runtime-dot" aria-hidden="true"></span>
        <span class="runtime-text" id="runtimeText">Python 待命</span>
      </div>
      <button class="btn ghost" id="loadBtn" type="button">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
          <path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5M5 15.5v3A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5v-3"/>
        </svg>
        載入 .py
      </button>
      <input type="file" id="fileInput" accept=".py,.txt,text/x-python" hidden>
    </div>
  </header>

  <main class="workspace">
    <aside class="panel examples" aria-label="範例">
      <div class="panel-head">
        <h2>範例</h2>
        <span class="panel-count" id="exampleCount">04</span>
      </div>
      <div class="example-list" id="exampleList">
        <div class="example-skeleton"></div>
        <div class="example-skeleton"></div>
        <div class="example-skeleton"></div>
      </div>
      <div class="examples-foot">
        <span class="drop-dot"></span>
        拖放 .py 到畫布也可以載入
      </div>
    </aside>

    <section class="panel editor-panel" aria-label="Python 編輯器">
      <div class="panel-head">
        <div class="file-meta">
          <span class="file-dot" aria-hidden="true"></span>
          <span class="file-name" id="fileName">未命名.py</span>
          <span class="state-pill" id="fileState" data-state="example">範例</span>
        </div>
        <div class="editor-actions">
          <button class="icon-btn" id="revertBtn" type="button" title="回到範例程式" aria-label="回到範例程式" disabled>
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
              <path d="M4.5 10.5a7.5 7.5 0 1 1 2.1 5.3M4.5 10.5V5.5m0 5h5"/>
            </svg>
          </button>
          <button class="btn run" id="runBtn" type="button" title="執行 (Ctrl / ⌘ + Enter)">
            <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
              <path d="M8.5 5.4v13.2L19 12 8.5 5.4Z"/>
            </svg>
            執行
            <kbd>⌘↵</kbd>
          </button>
        </div>
      </div>
      <div class="editor-wrap" id="editorWrap">
        <textarea id="editor" spellcheck="false" aria-label="Python 程式碼"></textarea>
      </div>
      <div class="editor-status">
        <span class="status-left"><span class="lang-dot"></span>Python · FreeCAD Part API</span>
        <span class="status-right" id="cursorInfo">Ln 1, Col 1</span>
      </div>
    </section>

    <section class="panel preview-panel" aria-label="3D 預覽畫布">
      <div class="panel-head">
        <div class="preview-meta">
          <span class="live-dot" aria-hidden="true"></span>
          <h2>預覽</h2>
          <span class="object-count" id="objectCount">0 個物件</span>
        </div>
        <div class="view-actions">
          <button class="icon-btn is-on" id="autoBtn" type="button" title="自動旋轉" aria-label="自動旋轉" aria-pressed="true">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
              <path d="M20 10.5a8 8 0 0 0-14.6-3M4 13.5a8 8 0 0 0 14.6 3M4 4.5v4h4M20 19.5v-4h-4"/>
            </svg>
          </button>
          <button class="icon-btn" id="resetBtn" type="button" title="重設視角" aria-label="重設視角">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
              <path d="m12 3 8 4.5v9L12 21l-8-4.5v-9L12 3Z"/>
              <path d="m4 7.5 8 4.5 8-4.5M12 12v9"/>
            </svg>
          </button>
        </div>
      </div>

      <div class="canvas-wrap" id="canvasWrap">
        <canvas id="view" aria-label="3D 模型預覽"></canvas>
        <div class="canvas-hint" id="canvasHint">
          <span class="mouse-icon" aria-hidden="true"></span>
          拖曳旋轉 · 滾輪縮放
        </div>

        <div class="canvas-loading" id="loadingState" hidden>
          <span class="spinner" aria-hidden="true"></span>
          <div>
            <strong>正在執行 Python</strong>
            <span id="loadingText">首次執行會下載執行環境…</span>
          </div>
        </div>

        <div class="canvas-empty" id="emptyState" hidden>
          <span class="empty-mark" aria-hidden="true">∅</span>
          <strong>沒有可顯示的物件</strong>
          <span id="emptyHint">在程式裡呼叫 <code>Part.show(shape)</code></span>
        </div>

        <div class="canvas-error" id="errorState" hidden>
          <div class="error-card">
            <div class="error-head">
              <span>無法執行這段程式</span>
              <button class="icon-btn tiny" id="dismissError" type="button" aria-label="關閉錯誤">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true">
                  <path d="m6 6 12 12M18 6 6 18"/>
                </svg>
              </button>
            </div>
            <pre id="errorText"></pre>
            <p>支援 FreeCAD / Part 的基本 Part API。</p>
          </div>
        </div>

        <div class="toast" id="toast" role="status" aria-live="polite" hidden></div>
      </div>
    </section>
  </main>

  <div class="drop-overlay" id="dropOverlay" hidden>
    <div class="drop-card">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
        <path d="M12 16V4m0 0L7.5 8.5M12 4l4.5 4.5M5 15.5v3A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5v-3"/>
      </svg>
      <strong>放開以載入 .py</strong>
      <span>檔案會直接開啟在編輯器</span>
    </div>
  </div>

  <script src="vendor/codemirror/codemirror.js"></script>
  <script src="vendor/codemirror/python.js"></script>
  <script src="vendor/codemirror/closebrackets.js"></script>
  <script src="vendor/codemirror/matchbrackets.js"></script>
  <script src="vendor/codemirror/active-line.js"></script>
  <script type="module" src="app.js"></script>
</body>
</html>