picoride 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.claude/launch.json +11 -0
- data/CHANGELOG.md +5 -0
- data/CLAUDE.md +1518 -0
- data/CODE_OF_CONDUCT.md +10 -0
- data/Dockerfile +76 -0
- data/LICENSE.txt +21 -0
- data/README.md +76 -0
- data/Rakefile +12 -0
- data/docker/esp32/Dockerfile +82 -0
- data/docker/pico/Dockerfile +77 -0
- data/exe/picoride +16 -0
- data/lib/picoride/background_job.rb +71 -0
- data/lib/picoride/project_store.rb +133 -0
- data/lib/picoride/r2p2_state.rb +137 -0
- data/lib/picoride/server.rb +1156 -0
- data/lib/picoride/version.rb +5 -0
- data/lib/picoride.rb +11 -0
- data/sig/picoruby/esp32/ide.rbs +8 -0
- data/web/funicular/README.md +111 -0
- data/web/funicular/ruby/components/directory_browser_dialog.rb +212 -0
- data/web/funicular/ruby/components/editor_app.rb +1560 -0
- data/web/funicular/ruby/components/log_panel.rb +102 -0
- data/web/funicular/ruby/components/menu_bar.rb +266 -0
- data/web/funicular/ruby/components/project_settings_dialog.rb +203 -0
- data/web/funicular/ruby/components/prompt_dialog.rb +76 -0
- data/web/funicular/ruby/components/toolbar.rb +30 -0
- data/web/funicular/ruby/components/tree_view.rb +112 -0
- data/web/funicular/ruby/main.rb +3 -0
- data/web/public/css/style.css +886 -0
- data/web/public/js/editor.js +257 -0
- data/web/views/index.erb +94 -0
- metadata +132 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# 汎用的なツリー表示コンポーネント。「ファイル一覧」に限らず、
|
|
2
|
+
# name/path/type/childrenを持つノードの配列であれば何でも描画できるように
|
|
3
|
+
# 作ってある(あとで独立したコンポーネント/gemとして切り出すことを想定した設計)。
|
|
4
|
+
# ファイルパスの組み立てや読み込み中/空表示のような呼び出し側固有の事情は
|
|
5
|
+
# 一切知らない、純粋な表示コンポーネントにしてある。
|
|
6
|
+
#
|
|
7
|
+
# ノードの形(Hash、シンボルキー):
|
|
8
|
+
# ディレクトリ: { name: "app", path: "app", type: :dir, children: [...] }
|
|
9
|
+
# ファイル : { name: "app.rb", path: "app/app.rb", type: :file }
|
|
10
|
+
# children は同じ形のノードの配列。ソート順など「どの順で並べるか」は
|
|
11
|
+
# 呼び出し側がnodesを組み立てる時点で決めておくこと(このコンポーネントは
|
|
12
|
+
# 渡された順にそのまま描画するだけ)。
|
|
13
|
+
#
|
|
14
|
+
# props:
|
|
15
|
+
# nodes: トップレベルのノード配列(必須)
|
|
16
|
+
# collapsed: 折りたたみ中のディレクトリのpath配列(省略時は全部展開表示)
|
|
17
|
+
# selected: 選択中(強調表示したい)ファイルのpath
|
|
18
|
+
# icon_for: ノードを受け取り { class:, label: } または nil を返すlambda(省略可、
|
|
19
|
+
# nilを返すか未指定ならアイコンを描画しない)
|
|
20
|
+
# on_select: ファイルノードがクリックされたときに path を渡して呼ばれるlambda
|
|
21
|
+
# on_toggle: ディレクトリノードがクリックされたときに path を渡して呼ばれるlambda
|
|
22
|
+
# on_context_menu: ノード(ファイル/ディレクトリどちらも)が右クリックされたときに
|
|
23
|
+
# (node, event) を渡して呼ばれるlambda(省略可)。どんなメニュー
|
|
24
|
+
# 項目を出すかはこのコンポーネントは一切知らず、呼び出し側が
|
|
25
|
+
# nodeの中身(type/path)を見て判断する
|
|
26
|
+
#
|
|
27
|
+
# 折りたたみ状態そのものはこのコンポーネントの中には持たせていない。
|
|
28
|
+
# Funicularは呼び出し側(親)が再描画されるたびにこの子コンポーネントを
|
|
29
|
+
# 作り直す(initialize_stateを呼び直す)ため、「一度だけ初期化して使い回したい
|
|
30
|
+
# 状態」を持たせると壊れる(実際にターミナル機能でこれを踏んだ。詳細は
|
|
31
|
+
# editor_app.rbやCLAUDE.md参照)。折りたたみ状態は必ず呼び出し側のstateに
|
|
32
|
+
# 持たせ、props経由で渡すこと。
|
|
33
|
+
class TreeView < Funicular::Component
|
|
34
|
+
def render
|
|
35
|
+
render_children(props[:nodes] || [], top_level: true)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def render_children(nodes, top_level: false)
|
|
41
|
+
ul(class: top_level ? 'tree-view' : 'tree-children') do
|
|
42
|
+
nodes.each { |node| render_node(node) }
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def render_node(node)
|
|
47
|
+
if node[:type] == :dir
|
|
48
|
+
render_dir_node(node)
|
|
49
|
+
else
|
|
50
|
+
render_file_node(node)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def render_dir_node(node)
|
|
55
|
+
path = node[:path]
|
|
56
|
+
collapsed = collapsed?(path)
|
|
57
|
+
|
|
58
|
+
li(key: path, class: 'tree-node tree-node-dir') do
|
|
59
|
+
div(class: 'tree-row', onclick: -> { toggle(path) }, oncontextmenu: ->(event) { context_menu(node, event) }) do
|
|
60
|
+
span(class: 'tree-caret') { collapsed ? '▸' : '▾' }
|
|
61
|
+
render_icon(node)
|
|
62
|
+
span(class: 'tree-label') { node[:name] }
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
render_children(node[:children] || []) unless collapsed
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def render_file_node(node)
|
|
70
|
+
path = node[:path]
|
|
71
|
+
classes = path == props[:selected] ? 'tree-node tree-node-file active' : 'tree-node tree-node-file'
|
|
72
|
+
|
|
73
|
+
li(key: path, class: classes) do
|
|
74
|
+
div(class: 'tree-row', onclick: -> { select_file(path) }, oncontextmenu: ->(event) { context_menu(node, event) }) do
|
|
75
|
+
span(class: 'tree-caret-spacer')
|
|
76
|
+
render_icon(node)
|
|
77
|
+
span(class: 'tree-label') { node[:name] }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def render_icon(node)
|
|
83
|
+
icon_for = props[:icon_for]
|
|
84
|
+
return unless icon_for
|
|
85
|
+
|
|
86
|
+
icon = icon_for.call(node)
|
|
87
|
+
return unless icon
|
|
88
|
+
|
|
89
|
+
span(class: "tree-icon #{icon[:class]}") { icon[:label].to_s }
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def collapsed?(path)
|
|
93
|
+
(props[:collapsed] || []).include?(path)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def toggle(path)
|
|
97
|
+
on_toggle = props[:on_toggle]
|
|
98
|
+
on_toggle.call(path) if on_toggle
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def select_file(path)
|
|
102
|
+
on_select = props[:on_select]
|
|
103
|
+
on_select.call(path) if on_select
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# ブラウザ標準の右クリックメニューは常に抑止し、呼び出し側にnode/eventを渡すだけ。
|
|
107
|
+
def context_menu(node, event)
|
|
108
|
+
event.preventDefault
|
|
109
|
+
on_context_menu = props[:on_context_menu]
|
|
110
|
+
on_context_menu.call(node, event) if on_context_menu
|
|
111
|
+
end
|
|
112
|
+
end
|