Skip to content

Commit 0339998

Browse files
author
art.aquino
committed
Add RepoConcat component and integrate into App.vue and NodePalette.vue
1 parent 7e7b7a4 commit 0339998

File tree

4 files changed

+324
-0
lines changed

4 files changed

+324
-0
lines changed

frontend/src/App.vue

+4
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@
9999
@node-resized="updateNodeDimensions"
100100
/>
101101
</template>
102+
<template #node-repoConcatNode="repoConcatNodeProps">
103+
<RepoConcat v-bind="repoConcatNodeProps" />
104+
</template>
102105

103106
<SaveRestoreControls @save="onSave" @restore="onRestore" />
104107
<LayoutControls
@@ -166,6 +169,7 @@ import DatadogNode from './components/DatadogNode.vue';
166169
import DatadogGraphNode from './components/DatadogGraphNode.vue';
167170
import TokenCounterNode from './components/TokenCounterNode.vue';
168171
import FlowControl from './components/FlowControl.vue';
172+
import RepoConcat from './components/RepoConcat.vue';
169173
170174
// --- SETUP ---
171175
interface BgColorInterface {

frontend/src/components/NodePalette.vue

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const nodeCategories = {
8989
"datadogGraphNode": null,
9090
},
9191
"Misc": {
92+
"repoConcatNode": null,
9293
"flowControlNode": null,
9394
"textNode": null,
9495
"textSplitterNode": null,
+315
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
<template>
2+
<div :style="data.style" class="node-container tool-node">
3+
<!-- Node label -->
4+
<div class="node-label">
5+
<input
6+
v-model="label"
7+
@change="updateNodeData"
8+
class="label-input"
9+
:style="data.labelStyle"
10+
/>
11+
</div>
12+
13+
<!-- Checkbox to enable/disable updating input from a connected source -->
14+
<div class="input-field">
15+
<input
16+
type="checkbox"
17+
:id="`${data.id}-update-from-source`"
18+
v-model="updateFromSource"
19+
@change="updateNodeData"
20+
/>
21+
<label :for="`${data.id}-update-from-source`" class="input-label">
22+
Update Input from Source
23+
</label>
24+
</div>
25+
26+
<!-- Input for Paths (comma separated) -->
27+
<div class="input-field">
28+
<label :for="`${data.id}-paths`" class="input-label">
29+
Paths (comma separated):
30+
</label>
31+
<input
32+
type="text"
33+
:id="`${data.id}-paths`"
34+
v-model="paths"
35+
@change="updateNodeData"
36+
class="input-text"
37+
/>
38+
</div>
39+
40+
<!-- Input for Types (comma separated) -->
41+
<div class="input-field">
42+
<label :for="`${data.id}-types`" class="input-label">
43+
Types (comma separated):
44+
</label>
45+
<input
46+
type="text"
47+
:id="`${data.id}-types`"
48+
v-model="types"
49+
@change="updateNodeData"
50+
class="input-text"
51+
/>
52+
</div>
53+
54+
<!-- Checkbox for Recursive -->
55+
<div class="input-field">
56+
<input
57+
type="checkbox"
58+
:id="`${data.id}-recursive`"
59+
v-model="recursive"
60+
@change="updateNodeData"
61+
/>
62+
<label :for="`${data.id}-recursive`" class="input-label">
63+
Recursive
64+
</label>
65+
</div>
66+
67+
<!-- Input for Ignore Pattern -->
68+
<div class="input-field">
69+
<label :for="`${data.id}-ignore`" class="input-label">
70+
Ignore Pattern:
71+
</label>
72+
<input
73+
type="text"
74+
:id="`${data.id}-ignore`"
75+
v-model="ignorePattern"
76+
@change="updateNodeData"
77+
class="input-text"
78+
/>
79+
</div>
80+
81+
<!-- Node connection handles -->
82+
<Handle v-if="data.hasInputs" type="target" position="left" id="input" />
83+
<Handle v-if="data.hasOutputs" type="source" position="right" id="output" />
84+
</div>
85+
</template>
86+
87+
<script setup>
88+
import { ref, computed, onMounted } from 'vue'
89+
import { Handle, useVueFlow } from '@vue-flow/core'
90+
91+
const { getEdges, findNode } = useVueFlow()
92+
93+
// Define the component props; note that we set default node data values for our inputs.
94+
const props = defineProps({
95+
id: {
96+
type: String,
97+
required: false,
98+
default: 'RepoConcat_0',
99+
},
100+
data: {
101+
type: Object,
102+
required: false,
103+
default: () => ({
104+
style: {},
105+
labelStyle: {},
106+
type: 'RepoConcatNode',
107+
inputs: {
108+
// Defaults: a single path and a comma‐separated list of file types
109+
paths: ".",
110+
types: ".go, .md, .vue, .js, .css, .html",
111+
recursive: false,
112+
ignorePattern: ""
113+
},
114+
outputs: {},
115+
hasInputs: true,
116+
hasOutputs: true,
117+
updateFromSource: true,
118+
}),
119+
},
120+
})
121+
122+
const emit = defineEmits(['update:data'])
123+
124+
// Local reactive variable for the update-from-source checkbox
125+
const updateFromSource = ref(props.data.updateFromSource)
126+
127+
// Mount the run method on the node data so that VueFlow can invoke it.
128+
onMounted(() => {
129+
if (!props.data.run) {
130+
props.data.run = run
131+
}
132+
})
133+
134+
/**
135+
* run() gathers input parameters either from connected nodes (if any and enabled)
136+
* or from this node’s own inputs, sends a POST request to /api/repoconcat,
137+
* and then updates the node’s outputs with the returned concatenated result.
138+
*/
139+
async function run() {
140+
try {
141+
// Clear previous output
142+
props.data.outputs.result = ''
143+
144+
// Check for connected source nodes (to optionally update input parameters)
145+
const connectedSources = getEdges.value
146+
.filter(edge => edge.target === props.id)
147+
.map(edge => edge.source)
148+
149+
let payload
150+
if (connectedSources.length > 0 && updateFromSource.value) {
151+
const sourceData = findNode(connectedSources[0]).data.outputs.result.output
152+
console.log('Connected source data:', sourceData)
153+
try {
154+
payload = JSON.parse(sourceData)
155+
} catch (err) {
156+
console.error('Error parsing JSON from connected node:', err)
157+
props.data.outputs.result = { error: 'Invalid JSON from connected node' }
158+
return { error: 'Invalid JSON from connected node' }
159+
}
160+
} else {
161+
// Use the values entered in this node's input fields.
162+
const pathsInput = props.data.inputs.paths
163+
const typesInput = props.data.inputs.types
164+
const recursiveValue = props.data.inputs.recursive
165+
const ignorePatternValue = props.data.inputs.ignorePattern
166+
167+
// Convert comma-separated strings into arrays.
168+
const pathsArray = pathsInput.split(',').map(s => s.trim()).filter(s => s)
169+
const typesArray = typesInput.split(',').map(s => s.trim()).filter(s => s)
170+
171+
payload = {
172+
paths: pathsArray,
173+
types: typesArray,
174+
recursive: recursiveValue,
175+
ignorePattern: ignorePatternValue,
176+
}
177+
}
178+
179+
// POST the parameters to the /api/repoconcat endpoint.
180+
const response = await fetch('http://localhost:8080/api/repoconcat', {
181+
method: 'POST',
182+
headers: { 'Content-Type': 'application/json' },
183+
body: JSON.stringify(payload),
184+
})
185+
186+
if (!response.ok) {
187+
const errorMsg = await response.text()
188+
console.error('Error response from server:', errorMsg)
189+
props.data.outputs.result = { error: errorMsg }
190+
return { error: errorMsg }
191+
}
192+
193+
// The API returns plain text – the concatenated output.
194+
const result = await response.text()
195+
console.log('RepoConcat run result:', result)
196+
197+
props.data.outputs = {
198+
result: {
199+
output: result,
200+
},
201+
}
202+
203+
updateNodeData()
204+
return { response, result }
205+
} catch (error) {
206+
console.error('Error in run():', error)
207+
props.data.outputs.result = { error: error.message }
208+
return { error }
209+
}
210+
}
211+
212+
// Computed property for the node label.
213+
const label = computed({
214+
get: () => props.data.type,
215+
set: (value) => {
216+
props.data.type = value
217+
updateNodeData()
218+
},
219+
})
220+
221+
// Computed property for the "paths" input.
222+
const paths = computed({
223+
get: () => props.data.inputs?.paths || '',
224+
set: (value) => {
225+
props.data.inputs.paths = value
226+
updateNodeData()
227+
},
228+
})
229+
230+
// Computed property for the "types" input.
231+
const types = computed({
232+
get: () => props.data.inputs?.types || '',
233+
set: (value) => {
234+
props.data.inputs.types = value
235+
updateNodeData()
236+
},
237+
})
238+
239+
// Computed property for the "recursive" checkbox.
240+
const recursive = computed({
241+
get: () => props.data.inputs?.recursive || false,
242+
set: (value) => {
243+
props.data.inputs.recursive = value
244+
updateNodeData()
245+
},
246+
})
247+
248+
// Computed property for the "ignorePattern" input.
249+
const ignorePattern = computed({
250+
get: () => props.data.inputs?.ignorePattern || '',
251+
set: (value) => {
252+
props.data.inputs.ignorePattern = value
253+
updateNodeData()
254+
},
255+
})
256+
257+
// Emit updated node data to VueFlow.
258+
function updateNodeData() {
259+
const updatedData = {
260+
...props.data,
261+
inputs: {
262+
paths: paths.value,
263+
types: types.value,
264+
recursive: recursive.value,
265+
ignorePattern: ignorePattern.value,
266+
},
267+
outputs: props.data.outputs,
268+
updateFromSource: updateFromSource.value,
269+
}
270+
emit('update:data', { id: props.id, data: updatedData })
271+
}
272+
</script>
273+
274+
<style scoped>
275+
.tool-node {
276+
--node-border-color: #777 !important;
277+
--node-bg-color: #1e1e1e !important;
278+
--node-text-color: #eee;
279+
}
280+
281+
.node-label {
282+
color: var(--node-text-color);
283+
font-size: 16px;
284+
text-align: center;
285+
margin-bottom: 10px;
286+
font-weight: bold;
287+
}
288+
289+
.input-field {
290+
margin-bottom: 8px;
291+
}
292+
293+
/* Styling for standard text inputs */
294+
.input-text {
295+
background-color: #333;
296+
border: 1px solid #666;
297+
color: #eee;
298+
padding: 4px;
299+
font-size: 12px;
300+
width: calc(100% - 8px);
301+
box-sizing: border-box;
302+
}
303+
304+
/* You can keep using your existing label-input styling from your other components */
305+
.label-input {
306+
background-color: #333;
307+
border: 1px solid #666;
308+
color: #eee;
309+
padding: 4px;
310+
font-size: 16px;
311+
width: 100%;
312+
box-sizing: border-box;
313+
}
314+
</style>
315+

frontend/src/useDnd.js

+4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import DatadogNode from './components/DatadogNode.vue'
1818
import DatadogGraphNode from './components/DatadogGraphNode.vue'
1919
import TokenCounterNode from './components/TokenCounterNode.vue'
2020
import FlowControl from './components/FlowControl.vue'
21+
import RepoConcat from './components/RepoConcat.vue'
2122

2223
let id = 0
2324

@@ -157,6 +158,9 @@ export default function useDragAndDrop() {
157158
case 'flowControlNode':
158159
component = FlowControl;
159160
break;
161+
case 'repoConcatNode':
162+
component = RepoConcat;
163+
break;
160164
default:
161165
console.error(`Unknown node type: ${draggedType.value}`);
162166
return;

0 commit comments

Comments
 (0)