Skip to content

Expose a keyboard shortcut to copy the Dart app ID #2271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion dwds/debug_extension_mv3/web/background.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ void _registerListeners() {
chrome.webNavigation.onCommitted
.addListener(allowInterop(_detectNavigationAwayFromDartApp));

chrome.commands.onCommand
.addListener(allowInterop(_maybeSendCopyAppIdRequest));

// Detect clicks on the Dart Debug Extension icon.
onExtensionIconClicked(
allowInterop(
Expand All @@ -67,7 +70,6 @@ void _registerListeners() {
Future<void> _handleRuntimeMessages(
dynamic jsRequest,
MessageSender sender,
// ignore: avoid-unused-parameters
Function sendResponse,
) async {
if (jsRequest is! String) return;
Expand Down Expand Up @@ -155,6 +157,8 @@ Future<void> _handleRuntimeMessages(
_setWarningIcon();
},
);

sendResponse(defaultResponse);
}

Future<void> _detectNavigationAwayFromDartApp(
Expand Down Expand Up @@ -206,6 +210,23 @@ DebugInfo _addTabInfo(DebugInfo debugInfo, {required Tab tab}) {
);
}

Future<bool> _maybeSendCopyAppIdRequest(String command, [Tab? tab]) async {
if (command != 'copyAppId') return false;
final tabId = (tab ?? await activeTab)?.id;
if (tabId == null) return false;
final debugInfo = await _fetchDebugInfo(tabId);
final workspaceName = debugInfo?.workspaceName;
if (workspaceName == null) return false;
final appId = '$workspaceName-$tabId';
return sendTabsMessage(
tabId: tabId,
type: MessageType.appId,
body: appId,
sender: Script.background,
recipient: Script.copier,
);
}

Future<void> _updateIcon(int activeTabId) async {
final debugInfo = await _fetchDebugInfo(activeTabId);
if (debugInfo == null) {
Expand Down
24 changes: 23 additions & 1 deletion dwds/debug_extension_mv3/web/chrome_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ external Chrome get chrome;
@JS()
@anonymous
class Chrome {
external Commands get commands;
external Debugger get debugger;
external Devtools get devtools;
external Notifications get notifications;
Expand All @@ -25,6 +26,20 @@ class Chrome {
/// chrome.debugger APIs:
/// https://developer.chrome.com/docs/extensions/reference/debugger

@JS()
@anonymous
class Commands {
external OnCommandHandler get onCommand;
}

@JS()
@anonymous
class OnCommandHandler {
external void addListener(
void Function(String commandName, [Tab? tab]) callback,
);
}

@JS()
@anonymous
class Debugger {
Expand Down Expand Up @@ -228,7 +243,7 @@ class ConnectionHandler {
@anonymous
class OnMessageHandler {
external void addListener(
void Function(dynamic, MessageSender, Function) callback,
dynamic Function(dynamic, MessageSender, Function) callback,
);
}

Expand Down Expand Up @@ -299,6 +314,13 @@ class Tabs {

external dynamic remove(int tabId, void Function()? callback);

external Object sendMessage(
int tabId,
Object? message,
Object? options,
void Function() callback,
);

external OnActivatedHandler get onActivated;

external OnRemovedHandler get onRemoved;
Expand Down
55 changes: 55 additions & 0 deletions dwds/debug_extension_mv3/web/copier.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

@JS()
library copier;

import 'dart:html';

import 'package:js/js.dart';

import 'chrome_api.dart';
import 'messaging.dart';

void main() {
_registerListeners();
}

void _registerListeners() {
chrome.runtime.onMessage.addListener(
allowInterop(_handleRuntimeMessages),
);
}

void _handleRuntimeMessages(
dynamic jsRequest,
MessageSender sender,
Function sendResponse,
) {
interceptMessage<String>(
message: jsRequest,
expectedType: MessageType.appId,
expectedSender: Script.background,
expectedRecipient: Script.copier,
messageHandler: _copyAppId,
);

sendResponse(defaultResponse);
}

void _copyAppId(String appId) {
final clipboard = window.navigator.clipboard;
if (clipboard == null) return;
clipboard.writeText(appId);
_showCopiedMessage(appId);
}

Future<void> _showCopiedMessage(String appId) async {
final snackbar = document.createElement('div');
snackbar.setInnerHtml('Copied app ID: <i>$appId</i>');
snackbar.classes.addAll(['snackbar', 'snackbar--info', 'show']);
document.body?.append(snackbar);
await Future.delayed(Duration(seconds: 4));
snackbar.remove();
}
12 changes: 11 additions & 1 deletion dwds/debug_extension_mv3/web/manifest_mv2.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,20 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["detector.dart.js"],
"js": ["detector.dart.js", "copier.dart.js"],
"css": ["static_assets/styles.css"],
"run_at": "document_end"
}
],
"commands": {
"copyAppId": {
"suggestedKey": {
"default": "Ctrl+Shift+7",
"mac": "Command+Shift+7"
},
"description": "Copy the app ID"
}
},
"web_accessible_resources": ["debug_info.dart.js"],
"options_page": "static_assets/settings.html"
}
12 changes: 11 additions & 1 deletion dwds/debug_extension_mv3/web/manifest_mv3.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,20 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["detector.dart.js"],
"js": ["detector.dart.js", "copier.dart.js"],
"css": ["static_assets/styles.css"],
"run_at": "document_end"
}
],
"commands": {
"copyAppId": {
"suggestedKey": {
"default": "Ctrl+Shift+7",
"mac": "Command+Shift+7"
},
"description": "Copy the app ID"
}
},
"web_accessible_resources": [
{
"matches": ["<all_urls>"],
Expand Down
86 changes: 69 additions & 17 deletions dwds/debug_extension_mv3/web/messaging.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,23 @@ library messaging;

import 'dart:async';
import 'dart:convert';
import 'dart:js_util';

import 'package:js/js.dart';

import 'chrome_api.dart';
import 'data_serializers.dart';
import 'logger.dart';

// A default response for the sendResponse callback.
//
// Prevents the message port from closing. See:
// https://developer.chrome.com/docs/extensions/mv3/messaging/#simple
final defaultResponse = jsify({'response': 'received'});

enum Script {
background,
copier,
debuggerPanel,
detector;

Expand All @@ -27,6 +35,7 @@ enum Script {
enum MessageType {
isAuthenticated,
connectFailure,
appId,
debugInfo,
debugStateChange,
devToolsUrl,
Expand Down Expand Up @@ -104,34 +113,77 @@ void interceptMessage<T>({
}
}

/// Send a message using the chrome.runtime.sendMessage API.
Future<bool> sendRuntimeMessage({
required MessageType type,
required String body,
required Script sender,
required Script recipient,
}) =>
_sendMessage(
type: type,
body: body,
sender: sender,
recipient: recipient,
);

/// Send a message using the chrome.tabs.sendMessage API.
Future<bool> sendTabsMessage({
required int tabId,
required MessageType type,
required String body,
required Script sender,
required Script recipient,
}) =>
_sendMessage(
tabId: tabId,
type: type,
body: body,
sender: sender,
recipient: recipient,
);

Future<bool> _sendMessage({
required MessageType type,
required String body,
required Script sender,
required Script recipient,
int? tabId,
}) {
final message = Message(
to: recipient,
from: sender,
type: type,
body: body,
);
).toJSON();
final completer = Completer<bool>();
chrome.runtime.sendMessage(
// id
null,
message.toJSON(),
// options
null,
allowInterop(() {
final error = chrome.runtime.lastError;
if (error != null) {
debugError(
'Error sending $type to $recipient from $sender: ${error.message}',
);
}
completer.complete(error != null);
}),
);
void responseHandler([dynamic _]) {
final error = chrome.runtime.lastError;
if (error != null) {
debugError(
'Error sending $type to $recipient from $sender: ${error.message}',
);
}
completer.complete(error != null);
}

if (tabId != null) {
chrome.tabs.sendMessage(
tabId,
message,
// options
null,
allowInterop(responseHandler),
);
} else {
chrome.runtime.sendMessage(
// id
null,
message,
// options
null,
allowInterop(responseHandler),
);
}
return completer.future;
}
10 changes: 6 additions & 4 deletions dwds/debug_extension_mv3/web/static_assets/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,16 @@ iframe {
}

.debugger-card > .mdl-card__title {
background: url("debugger_settings.png");
background-position: center;
background-repeat: no-repeat;
background: url("debugger_settings.png");
height: 200px;
}

.inspector-card > .mdl-card__title {
background: url("inspect_widget.png");
background-position: center;
background-repeat: no-repeat;
background: url("inspect_widget.png");
height: 300px;
}

Expand All @@ -75,20 +75,22 @@ h6 {
}

.snackbar {
border-radius: 2px;
bottom: 0px;
color: #eeeeee;
font-family: Roboto, 'Helvetica Neue', sans-serif;
left: 0px;
padding: 16px;
position: fixed;
right: 0px;
text-align: center;
visibility: hidden;
width: 100%;
z-index: 1;
}

.snackbar > a {
font-weight: bold;
color: #eeeeee;
font-weight: bold;
}

.snackbar--info {
Expand Down