chrome-ai-bridge 1.0.4 → 1.0.5
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.
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationRepository.js +163 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js +10 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/annotations.js +5 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/Prototypes.js +33 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/greendev/greendev.js +4 -0
- package/build/src/tools/gemini-web.js +23 -6
- package/package.json +1 -1
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
import * as Common from '../../core/common/common.js';
|
|
5
|
+
import * as GreenDev from '../greendev/greendev.js';
|
|
6
|
+
import { AnnotationType } from './AnnotationType.js';
|
|
7
|
+
export class AnnotationRepository {
|
|
8
|
+
static #instance = null;
|
|
9
|
+
static #hasRepliedGreenDevDisabled = false;
|
|
10
|
+
static #hasShownFlagWarning = false;
|
|
11
|
+
#events = new Common.ObjectWrapper.ObjectWrapper();
|
|
12
|
+
#annotationData = [];
|
|
13
|
+
#nextId = 0;
|
|
14
|
+
static instance() {
|
|
15
|
+
if (!AnnotationRepository.#instance) {
|
|
16
|
+
AnnotationRepository.#instance = new AnnotationRepository();
|
|
17
|
+
}
|
|
18
|
+
return AnnotationRepository.#instance;
|
|
19
|
+
}
|
|
20
|
+
static annotationsEnabled() {
|
|
21
|
+
const enabled = GreenDev.Prototypes.instance().isEnabled('aiAnnotations');
|
|
22
|
+
// TODO(finnur): Fix race when Repository is created before feature flags have been set properly.
|
|
23
|
+
if (!enabled) {
|
|
24
|
+
this.#hasRepliedGreenDevDisabled = true;
|
|
25
|
+
}
|
|
26
|
+
else if (this.#hasRepliedGreenDevDisabled && !this.#hasShownFlagWarning) {
|
|
27
|
+
console.warn('Flag controlling GreenDev has flipped from false to true. ' +
|
|
28
|
+
'Only some callers will expect GreenDev to be enabled, which can lead to unexpected results.');
|
|
29
|
+
this.#hasShownFlagWarning = true;
|
|
30
|
+
}
|
|
31
|
+
return Boolean(enabled);
|
|
32
|
+
}
|
|
33
|
+
addEventListener(eventType, listener, thisObject) {
|
|
34
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
35
|
+
console.warn('Received request to add event listener with annotations disabled');
|
|
36
|
+
}
|
|
37
|
+
return this.#events.addEventListener(eventType, listener, thisObject);
|
|
38
|
+
}
|
|
39
|
+
getAnnotationDataByType(type) {
|
|
40
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
41
|
+
console.warn('Received query for annotation types with annotations disabled');
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const annotations = this.#annotationData.filter(annotation => annotation.type === type);
|
|
45
|
+
return annotations;
|
|
46
|
+
}
|
|
47
|
+
getAnnotationDataById(id) {
|
|
48
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
49
|
+
console.warn('Received query for annotation type with annotations disabled');
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
return this.#annotationData.find(annotation => annotation.id === id);
|
|
53
|
+
}
|
|
54
|
+
#getExistingAnnotation(type, anchor) {
|
|
55
|
+
const annotations = this.getAnnotationDataByType(type);
|
|
56
|
+
const annotation = annotations.find(annotation => {
|
|
57
|
+
if (typeof anchor === 'string') {
|
|
58
|
+
return annotation.lookupId === anchor;
|
|
59
|
+
}
|
|
60
|
+
switch (type) {
|
|
61
|
+
case AnnotationType.ELEMENT_NODE: {
|
|
62
|
+
const elementAnnotation = annotation;
|
|
63
|
+
return elementAnnotation.anchor === anchor;
|
|
64
|
+
}
|
|
65
|
+
case AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS: {
|
|
66
|
+
const networkRequestDetailsAnnotation = annotation;
|
|
67
|
+
return networkRequestDetailsAnnotation.anchor === anchor;
|
|
68
|
+
}
|
|
69
|
+
default:
|
|
70
|
+
console.warn('[AnnotationRepository] Unknown AnnotationType', type);
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
return annotation;
|
|
75
|
+
}
|
|
76
|
+
#updateExistingAnnotationLabel(label, type, anchor) {
|
|
77
|
+
const annotation = this.#getExistingAnnotation(type, anchor);
|
|
78
|
+
if (annotation) {
|
|
79
|
+
// TODO(finnur): This should work for annotations that have not been displayed yet,
|
|
80
|
+
// but we need to also notify the AnnotationManager for those that have been shown.
|
|
81
|
+
annotation.message = label;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
addElementsAnnotation(label, anchor, anchorToString) {
|
|
87
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
88
|
+
console.warn('Received annotation registration with annotations disabled');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (this.#updateExistingAnnotationLabel(label, AnnotationType.ELEMENT_NODE, anchor)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const annotationData = {
|
|
95
|
+
id: this.#nextId++,
|
|
96
|
+
type: AnnotationType.ELEMENT_NODE,
|
|
97
|
+
message: label,
|
|
98
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
99
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
100
|
+
anchorToString,
|
|
101
|
+
};
|
|
102
|
+
this.#annotationData.push(annotationData);
|
|
103
|
+
// eslint-disable-next-line no-console
|
|
104
|
+
console.log('[AnnotationRepository] Added element annotation:', label, {
|
|
105
|
+
annotationData,
|
|
106
|
+
annotations: this.#annotationData.length,
|
|
107
|
+
});
|
|
108
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationData);
|
|
109
|
+
}
|
|
110
|
+
addNetworkRequestAnnotation(label, anchor, anchorToString) {
|
|
111
|
+
if (!AnnotationRepository.annotationsEnabled()) {
|
|
112
|
+
console.warn('Received annotation registration with annotations disabled');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// We only need to update the NETWORK_REQUEST_SUBPANEL_HEADERS because the
|
|
116
|
+
// NETWORK_REQUEST Annotation has no meaningful label.
|
|
117
|
+
if (this.#updateExistingAnnotationLabel(label, AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS, anchor)) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const annotationData = {
|
|
121
|
+
id: this.#nextId++,
|
|
122
|
+
type: AnnotationType.NETWORK_REQUEST,
|
|
123
|
+
message: '',
|
|
124
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
125
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
126
|
+
anchorToString,
|
|
127
|
+
};
|
|
128
|
+
this.#annotationData.push(annotationData);
|
|
129
|
+
// eslint-disable-next-line no-console
|
|
130
|
+
console.log('[AnnotationRepository] Added annotation:', label, {
|
|
131
|
+
annotationData,
|
|
132
|
+
annotations: this.#annotationData.length,
|
|
133
|
+
});
|
|
134
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationData);
|
|
135
|
+
const annotationDetailsData = {
|
|
136
|
+
id: this.#nextId++,
|
|
137
|
+
type: AnnotationType.NETWORK_REQUEST_SUBPANEL_HEADERS,
|
|
138
|
+
message: label,
|
|
139
|
+
lookupId: typeof anchor === 'string' ? anchor : '',
|
|
140
|
+
anchor: typeof anchor !== 'string' ? anchor : undefined,
|
|
141
|
+
anchorToString,
|
|
142
|
+
};
|
|
143
|
+
this.#annotationData.push(annotationDetailsData);
|
|
144
|
+
this.#events.dispatchEventToListeners("AnnotationAdded" /* Events.ANNOTATION_ADDED */, annotationDetailsData);
|
|
145
|
+
}
|
|
146
|
+
deleteAllAnnotations() {
|
|
147
|
+
this.#annotationData = [];
|
|
148
|
+
this.#events.dispatchEventToListeners("AllAnnotationsDeleted" /* Events.ALL_ANNOTATIONS_DELETED */);
|
|
149
|
+
// eslint-disable-next-line no-console
|
|
150
|
+
console.log('[AnnotationRepository] Deleting all annotations');
|
|
151
|
+
}
|
|
152
|
+
deleteAnnotation(id) {
|
|
153
|
+
const index = this.#annotationData.findIndex(annotation => annotation.id === id);
|
|
154
|
+
if (index === -1) {
|
|
155
|
+
console.warn(`[AnnotationRepository] Could not find annotation with id ${id}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
this.#annotationData.splice(index, 1);
|
|
159
|
+
this.#events.dispatchEventToListeners("AnnotationDeleted" /* Events.ANNOTATION_DELETED */, { id });
|
|
160
|
+
// eslint-disable-next-line no-console
|
|
161
|
+
console.log(`[AnnotationRepository] Deleted annotation with id ${id}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/annotations/AnnotationType.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
export var AnnotationType;
|
|
5
|
+
(function (AnnotationType) {
|
|
6
|
+
AnnotationType[AnnotationType["ELEMENT_NODE"] = 0] = "ELEMENT_NODE";
|
|
7
|
+
AnnotationType[AnnotationType["STYLE_RULE"] = 1] = "STYLE_RULE";
|
|
8
|
+
AnnotationType[AnnotationType["NETWORK_REQUEST"] = 2] = "NETWORK_REQUEST";
|
|
9
|
+
AnnotationType[AnnotationType["NETWORK_REQUEST_SUBPANEL_HEADERS"] = 3] = "NETWORK_REQUEST_SUBPANEL_HEADERS";
|
|
10
|
+
})(AnnotationType || (AnnotationType = {}));
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2025 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
import * as Common from '../../core/common/common.js';
|
|
5
|
+
import * as Root from '../../core/root/root.js';
|
|
6
|
+
let instance = null;
|
|
7
|
+
export class Prototypes {
|
|
8
|
+
constructor() {
|
|
9
|
+
}
|
|
10
|
+
static instance() {
|
|
11
|
+
if (instance) {
|
|
12
|
+
return instance;
|
|
13
|
+
}
|
|
14
|
+
instance = new Prototypes();
|
|
15
|
+
return instance;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Returns true if the specific setting is turned on AND the GreenDev flag is enabled
|
|
19
|
+
*/
|
|
20
|
+
isEnabled(setting) {
|
|
21
|
+
const greendevFlagEnabled = Boolean(Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled);
|
|
22
|
+
return greendevFlagEnabled && this.settings()[setting].get();
|
|
23
|
+
}
|
|
24
|
+
settings() {
|
|
25
|
+
const settings = Common.Settings.Settings.instance();
|
|
26
|
+
const inDevToolsFloaty = settings.createSetting('greendev-in-devtools-floaty-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
|
|
27
|
+
const inlineWidgets = settings.createSetting('greendev-inline-widgets-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
|
|
28
|
+
const aiAnnotations = settings.createSetting('greendev-ai-annotations-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
|
|
29
|
+
const artifactViewer = settings.createSetting('greendev-artifact-viewer-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
|
|
30
|
+
const copyToGemini = settings.createSetting('greendev-copy-to-gemini-enabled', false, "Local" /* Common.Settings.SettingStorageType.LOCAL */);
|
|
31
|
+
return { inDevToolsFloaty, inlineWidgets, aiAnnotations, artifactViewer, copyToGemini };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -304,6 +304,10 @@ export const askGeminiWeb = defineTool({
|
|
|
304
304
|
return;
|
|
305
305
|
}
|
|
306
306
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
307
|
+
// 質問送信前に model-response 要素数を記録(ChatGPTと同じカウント方式)
|
|
308
|
+
const initialModelResponseCount = await page.evaluate(() => {
|
|
309
|
+
return document.querySelectorAll('model-response').length;
|
|
310
|
+
});
|
|
307
311
|
// Click send button - look for "プロンプトを送信" or similar
|
|
308
312
|
const sent = await page.evaluate(() => {
|
|
309
313
|
const buttons = Array.from(document.querySelectorAll('button'));
|
|
@@ -405,9 +409,17 @@ export const askGeminiWeb = defineTool({
|
|
|
405
409
|
});
|
|
406
410
|
return !!stopButton;
|
|
407
411
|
});
|
|
408
|
-
// Stop button/icon disappeared =
|
|
412
|
+
// Stop button/icon disappeared = check if new message appeared
|
|
409
413
|
if (!hasStopIndicator) {
|
|
410
|
-
|
|
414
|
+
// 追加: model-response 要素数が増えたか確認(ChatGPTと同じ方式)
|
|
415
|
+
const currentModelResponseCount = await page.evaluate(() => {
|
|
416
|
+
return document.querySelectorAll('model-response').length;
|
|
417
|
+
});
|
|
418
|
+
if (currentModelResponseCount > initialModelResponseCount) {
|
|
419
|
+
// ストップボタン消滅 AND 新規メッセージ出現で完了
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
// メッセージ数が増えていなければ、まだ待機続行
|
|
411
423
|
}
|
|
412
424
|
if (Date.now() - startTime > 180000) {
|
|
413
425
|
// 3 mins timeout
|
|
@@ -415,19 +427,24 @@ export const askGeminiWeb = defineTool({
|
|
|
415
427
|
break;
|
|
416
428
|
}
|
|
417
429
|
}
|
|
418
|
-
// Get the final response content
|
|
419
|
-
const responseText = await page.evaluate(
|
|
430
|
+
// Get the final response content (新規に追加された model-response のみを取得)
|
|
431
|
+
const responseText = await page.evaluate(initialCount => {
|
|
420
432
|
// Get content from model-response elements
|
|
421
433
|
const modelResponses = Array.from(document.querySelectorAll('model-response'));
|
|
434
|
+
if (modelResponses.length > initialCount) {
|
|
435
|
+
// 新規に追加された model-response を取得(ChatGPTと同じ方式)
|
|
436
|
+
const newResponse = modelResponses[initialCount];
|
|
437
|
+
return newResponse.textContent?.trim() || '';
|
|
438
|
+
}
|
|
439
|
+
// Fallback: get the last model response if any
|
|
422
440
|
if (modelResponses.length > 0) {
|
|
423
|
-
// Get the last model response
|
|
424
441
|
const lastResponse = modelResponses[modelResponses.length - 1];
|
|
425
442
|
return lastResponse.textContent?.trim() || '';
|
|
426
443
|
}
|
|
427
444
|
// Fallback: get text from main area
|
|
428
445
|
const main = document.querySelector('main');
|
|
429
446
|
return main?.innerText.slice(-5000) || '';
|
|
430
|
-
});
|
|
447
|
+
}, initialModelResponseCount);
|
|
431
448
|
response.appendResponseLine('✅ 回答完了');
|
|
432
449
|
// Always save/update session (not just for new chats)
|
|
433
450
|
const chatUrl = page.url();
|
package/package.json
CHANGED