Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
MOC 96 use dsl for editing rebased (#148)
Browse files Browse the repository at this point in the history
* Use Reactor to modify dom in chat

* Fix biome

* Prototype interfaces for Reactor

* Add highlighting to applied modification

* WIP: Use DSL for edits

* Support unapply in all DSL commands except replaceAll

* All tests passing for apply/unapply modifications

* Implement reactor interface

* First modifications working end-to-end

* Refactor reactor; Begin highlighter integration

* Integrate chat and edit

* Integrate highlighting

* fix typo in mocksi-lite/package.json

* remove buggy timestamp updater

* Update Reactor's README to match the current API

* disable timestamps and keep everything else the same for now

---------

Co-authored-by: Jonathan Kaplan <[email protected]>
Co-authored-by: Jonathan Kaplan <[email protected]>
  • Loading branch information
3 people authored Aug 19, 2024
1 parent e0e2795 commit fb1b0d5
Show file tree
Hide file tree
Showing 24 changed files with 1,746 additions and 537 deletions.
3 changes: 3 additions & 0 deletions apps/mocksi-lite/reactorSingleton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Reactor } from "@repo/reactor";

export default new Reactor();
43 changes: 0 additions & 43 deletions apps/mocksi-lite/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { DOMManipulator } from "@repo/dodom";
import { modifyHtml } from "@repo/reactor";
import auth0, { type WebAuth } from "auth0-js";
import sanitizeHtml from "sanitize-html";
import { debug } from "webpack";
import MocksiRollbar from "./MocksiRollbar";
import type { Alteration } from "./background";
import type { Recording } from "./background";
Expand Down Expand Up @@ -188,47 +186,6 @@ export const loadAlterations = async (
}
return selector;
}

const timestamps = getTimestamps();
const now = new Date();
await Promise.all(
timestamps.map(async (timestamp) => {
const userRequest = JSON.stringify({
modifications: [
{
action: "updateTimestampReferences",
selector: timestamp.selector,
timestampRef: {
currentTime: now.toISOString(),
recordedAt: createdAt?.toString(),
},
},
],
});
console.log("userRequest", userRequest);
const contents = document.querySelectorAll(timestamp.selector);
for (const content of contents) {
try {
const result = await modifyHtml(content.outerHTML, userRequest);
const parser = new DOMParser();
const doc = parser.parseFromString(result, "text/html");

if (doc.body) {
// Replace the original content with the modified content
content.outerHTML = doc.body.innerHTML;
} else {
console.error("Parsed document body is null or undefined");
}
} catch (error) {
console.error(
"Error updating innerHTML for",
timestamp.selector,
error,
);
}
}
}),
);
};

// This is from chrome.storage.local
Expand Down
123 changes: 75 additions & 48 deletions packages/reactor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,87 +18,111 @@ By abstracting these operations, Reactor helps maintain the integrity of the pag

Follow these steps to use Reactor in your project:

### Step 1: Import the Library
Sure, I'll create detailed usage instructions for the `Reactor` class based on `reactor.ts` and the provided example.

First, import the `modifyHtml` function from Reactor:
## Usage Instructions for Reactor

```typescript
import { modifyHtml } from 'reactor';
Reactor is a powerful tool for dynamically modifying HTML content on a webpage. Below are the steps to use the `Reactor` class effectively.

### Step 1: Import the Reactor Class

First, import the `Reactor` class from the `reactor` module.

```typescript
import { Reactor } from 'reactor';
```

### Step 2: Obtain the HTML Content
### Step 2: Create an Instance of Reactor

Next, get the HTML content you want to modify. This could be from:
Create a new instance of the `Reactor` class.

```typescript
const exampleReactor = new Reactor();
```

- A static HTML file
- The current webpage's HTML
- HTML received from a server
### Step 3: Attach Reactor to the Document

For example:
Attach the `Reactor` instance to the document you want to modify. This step is necessary for the `Reactor` to start generating events and applying modifications.

```typescript
const myHtml = `
<div id="user-info">Eliza Hart</div>
<div id="welcome-message">Welcome, Eliza!</div>
<img id="profile-pic" src="eliza.jpg" />
`;
await exampleReactor.attach(document, highlighter);
```

### Step 3: Define the Modifications
- `document`: The HTML document to which the `Reactor` should be attached.
- `highlighter`: An instance of the `Highlighter` class used for highlighting elements (optional).

Create an array of changes you want to make. Each change should include:
### Step 4: Define Modifications

Define the modifications you want to apply. Each modification should include:

- `selector`: A CSS selector to identify the element(s) to modify.
- `action`: The type of modification (e.g., replace, append, remove).
- Additional fields depending on the action (e.g., `content` for replacing text).

```typescript
const myChanges = [
{ selector: "#user-info", action: "replace", content: "Santiago Hart" },
const data = [
{ selector: "#user-info", action: "replace", content: "Santiago Hart" },
{ selector: "#welcome-message", action: "replace", content: "Welcome, Santiago!" },
{ selector: "#profile-pic", action: "swapImage", imageUrl: "santiago.jpg" },
{ selector: "body", action: "toast", toastMessage: "Welcome to the new site!" }
];
```

### Step 4: Create the User Request
### Step 5: Push Modifications

Combine your HTML and changes array into a "user request" object:
Push the modifications to the `Reactor` instance. This will apply the modifications to the document.

```typescript
const userRequest = JSON.stringify({
description: "Change name from Eliza to Santiago and show notification",
modifications: myChanges
});
```typescript
await exampleReactor.pushModification(data);
```

### Step 5: Modify the HTML Content
### Step 6: Export the Modified DOM

Call `modifyHtml` with your HTML and user request to get the modified HTML:
Export the modified DOM as an array of `DomJsonExportNode` objects. This can be useful for saving the modified state or further processing.

```typescript
async function updatePage() {
try {
const updatedHtml = await modifyHtml(myHtml, userRequest);
console.log(updatedHtml);
} catch (error) {
console.error("Modification failed:", error);
}
}

// Run the async function
updatePage();
const updatedDomJson = exampleReactor.exportDOM();
console.log(updatedDomJson);
```

This will produce the updated HTML with the changes applied:
### Example

```html
<div id="user-info">Santiago Hart</div>
<div id="welcome-message">Welcome, Santiago!</div>
<img id="profile-pic" src="santiago.jpg" />
<div class="toast">Welcome to the new site!</div>
Here is a complete example that demonstrates how to use the `Reactor` class:

```typescript
import { Reactor } from 'reactor';

// Step 1: Create an instance of Reactor
const exampleReactor = new Reactor();

// Step 2: Attach Reactor to the document
await exampleReactor.attach(document, highlighter);

// Step 3: Define modifications
const data = [
{ selector: "#user-info", action: "replace", content: "Santiago Hart" },
{ selector: "#welcome-message", action: "replace", content: "Welcome, Santiago!" },
{ selector: "#profile-pic", action: "swapImage", imageUrl: "santiago.jpg" },
{ selector: "body", action: "toast", toastMessage: "Welcome to the new site!" }
];

// Step 4: Push modifications
await exampleReactor.pushModification(data);

// Step 5: Export the modified DOM
const updatedDomJson = exampleReactor.exportDOM();
console.log(updatedDomJson);
```

### Additional Methods

- **detach(clearModifications = true)**: Detach the `Reactor` from the document, optionally clearing all applied modifications.
- **isAttached()**: Check if the `Reactor` is currently attached to a document.
- **getAppliedModifications()**: Get an iterable object of all applied modifications.
- **clearAppliedModifications()**: Clear all applied modifications.

By following these steps, you can effectively use the `Reactor` class to dynamically modify HTML content on a webpage.

## Supported Actions

Reactor supports several actions for modifying HTML content:
Expand All @@ -111,8 +135,10 @@ Reactor supports several actions for modifying HTML content:
| remove | Remove selected element(s) from the HTML | - |
| swapImage | Change the `src` URL of selected `<img>` element(s) | `imageUrl` |
| highlight | Apply a highlight effect to selected element(s) | `highlightStyle` (optional) |
| toast | Show a notification message | `toastMessage` |
| toast | Show a notification message | `toastMessage`, `duration` (optional) |
| addComponent| Insert a custom component | `componentHtml` |
| replaceAll | Replace all occurrences of a pattern in the content | `content` (pattern to replace) |
| unknown | Reserved for future use or custom actions | - |

## Handling Errors

Expand Down Expand Up @@ -185,6 +211,7 @@ The user request should be a JSON string with the following structure:
- `"highlight"`: Applies a custom style for highlighting the selected element(s).
- `"toast"`: Displays a toast notification with a specified message.
- `"addComponent"`: Adds a custom HTML component to the selected location.
- `"replaceAll"`: Replaces all occurrences of a pattern in the content.
- `"unknown"`: Reserved for future use or custom actions.

## Examples
Expand All @@ -203,7 +230,7 @@ The user request should be a JSON string with the following structure:
**Modification Request:**

```json
[{
{
"description": "Replace the user's name.",
"modifications": [
{
Expand All @@ -212,7 +239,7 @@ The user request should be a JSON string with the following structure:
"content": "John Doe"
}
]
}]
}
```

**After:**
Expand Down
10 changes: 8 additions & 2 deletions packages/reactor/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
export { modifyHtml } from "./main";
export type { Modification, ModificationRequest } from "./interfaces";
export type {
Modification,
ModificationRequest,
AppliedModifications,
DomJsonExportNode,
Highlighter,
} from "./interfaces";
export { Reactor } from "./reactor";
54 changes: 45 additions & 9 deletions packages/reactor/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,69 @@
interface TimeStampReference {
// NOTE: this is a iso8601 date string
recordedAt: string;
// NOTE: this is a iso8601 date string
currentTime: string;
}

export interface Modification {
selector?: string;
xpath?: string;
action:
| "replace"
| "replaceAll"
| "append"
| "prepend"
| "remove"
| "swapImage"
| "highlight"
| "toast"
| "addComponent"
| "updateTimestampReferences"
| "unknown";
content?: string;
imageUrl?: string;
toastMessage?: string;
componentHtml?: string;
highlightStyle?: string;
duration?: number;
timestampRef?: TimeStampReference;
}

export interface ModificationRequest {
description: string;
modifications: Modification[];
}

export interface AppliedModifications {
modificationRequest: ModificationRequest;

/**
* Turn highlighting on or off for the changes made
* by this request
*/
setHighlight(highlight: boolean): void;
}

export interface DomJsonExportNode {
tag: string;
visible: boolean;
text?: string;
attributes?: Record<string, string>;
children?: DomJsonExportNode[];
}

export interface Highlighter {
highlightNode(elementToHighlight: Node): void;
removeHighlightNode(elementToUnhighlight: Node): void;
}

export abstract class AppliableModification {
doc: Document;
highlightNodes: Node[] = [];

constructor(doc: Document) {
this.doc = doc;
}

abstract apply(): void;
abstract unapply(): void;

getHighlightNodes(): Node[] {
return this.highlightNodes;
}

addHighlightNode(node: Node): void {
this.highlightNodes.push(node);
}
}
Loading

0 comments on commit fb1b0d5

Please sign in to comment.