Skip to content

Commit

Permalink
Expose a keyboard shortcut to copy the Dart app ID (#2271)
Browse files Browse the repository at this point in the history
  • Loading branch information
elliette authored Oct 27, 2023
1 parent c26b4e5 commit b3d6ef1
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 25 deletions.
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

0 comments on commit b3d6ef1

Please sign in to comment.