Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
ueokande committed Dec 29, 2023
1 parent 9126824 commit 3baf48e
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 105 deletions.
51 changes: 23 additions & 28 deletions src/background/clients/HintClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,80 +12,75 @@ export type Size = {
};

export default interface HintClient {
countHints(
lookupTargets(
tabId: number,
frameId: number,
viewSize: Size,
framePosition: Point,
): Promise<number>;
): Promise<string[]>;

createHints(
assignTags(
tabId: number,
frameId: number,
hints: string[],
viewSize: Size,
framePosition: Point,
elementTags: Record<string, string>,
): Promise<void>;

filterHints(tabId: number, prefix: string): Promise<void>;
showHints(tabId: number, elements: string[]): Promise<void>;

clearHints(tabId: number): Promise<void>;

activateIfExists(
activate(
tabId: number,
tag: string,
frameId: number,
element: string,
newTab: boolean,
background: boolean,
): Promise<void>;
}

@injectable()
export class HintClientImpl implements HintClient {
countHints(
async lookupTargets(
tabId: number,
frameId: number,
viewSize: Size,
framePosition: Point,
): Promise<number> {
): Promise<string[]> {
const sender = newSender(tabId, frameId);
return sender.send("follow.count.hints", {
const { elements } = await sender.send("follow.lookup", {
viewSize,
framePosition,
});
return elements;
}

createHints(
assignTags(
tabId: number,
frameId: number,
hints: string[],
viewSize: Size,
framePosition: Point,
elementTags: Record<string, string>,
): Promise<void> {
const sender = newSender(tabId, frameId);
return sender.send("follow.create.hints", {
viewSize,
framePosition,
hints,
});
return sender.send("follow.assign", { elementTags });
}

filterHints(tabId: number, prefix: string): Promise<void> {
showHints(tabId: number, elements: string[]): Promise<void> {
const sender = newSender(tabId);
return sender.send("follow.filter.hints", { prefix });
return sender.send("follow.show", { elements });
}

clearHints(tabId: number): Promise<void> {
const sender = newSender(tabId);
return sender.send("follow.remove.hints");
return sender.send("follow.clear");
}

activateIfExists(
activate(
tabId: number,
hint: string,
frameId: number,
element: string,
newTab: boolean,
background: boolean,
): Promise<void> {
const sender = newSender(tabId);
return sender.send("follow.activate", { hint, newTab, background });
const sender = newSender(tabId, frameId);
return sender.send("follow.activate", { element, newTab, background });
}
}
38 changes: 23 additions & 15 deletions src/background/repositories/HintRepository.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import { injectable } from "inversify";
import LocalCache, { LocalCacheImpl } from "../db/LocalStorage";

export type HintTarget = {
frameId: number;
element: string;
tag: string;
};

export default interface HintRepository {
startHintMode(
opts: { newTab: boolean; background: boolean },
hints: string[],
targets: HintTarget[],
): Promise<void>;

getOption(): Promise<{ newTab: boolean; background: boolean }>;
Expand All @@ -13,9 +19,7 @@ export default interface HintRepository {

popKey(): Promise<void>;

getMatchedHints(): Promise<string[]>;

getKeys(): Promise<string>;
getMatchedHints(): Promise<HintTarget[]>;
}

type Option = {
Expand All @@ -25,7 +29,7 @@ type Option = {

type State = {
option: Option;
hints: string[];
hintMap: Record<string, HintTarget>; // tag -> target
keys: string[];
};

Expand All @@ -36,19 +40,27 @@ export class HintRepositoryImpl implements HintRepository {
HintRepositoryImpl.name,
{
option: { newTab: false, background: false },
hints: [],
hintMap: {},
keys: [],
},
),
) {}

startHintMode(
option: { newTab: boolean; background: boolean },
hints: string[],
hints: HintTarget[],
): Promise<void> {
const hintMap = hints.reduce(
(acc, hint) => {
acc[hint.tag] = hint;
return acc;
},
{} as Record<string, HintTarget>,
);

const state: State = {
option,
hints,
hintMap,
keys: [],
};
return this.cache.setValue(state);
Expand All @@ -71,14 +83,10 @@ export class HintRepositoryImpl implements HintRepository {
await this.cache.setValue(state);
}

async getMatchedHints(): Promise<string[]> {
async getMatchedHints(): Promise<HintTarget[]> {
const state = await this.cache.getValue();
const prefix = state.keys.join("");
return state.hints.filter((t) => t.startsWith(prefix));
}

async getKeys(): Promise<string> {
const { keys } = await this.cache.getValue();
return keys.join("");
const tags = Object.keys(state.hintMap).filter((t) => t.startsWith(prefix));
return tags.map((t) => state.hintMap[t]);
}
}
34 changes: 23 additions & 11 deletions src/background/usecases/HintKeyUseCase.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { injectable, inject } from "inversify";
import HintClient from "../clients/HintClient";
import HintRepository from "../repositories/HintRepository";
import HintRepository, {
type HintTarget,
} from "../repositories/HintRepository";

@injectable()
export default class HintKeyUseCase {
Expand All @@ -13,39 +15,49 @@ export default class HintKeyUseCase {

async pressKey(tabId: number, key: string): Promise<boolean> {
switch (key) {
case "Enter":
await this.activate(tabId, await this.hintRepository.getKeys());
case "Enter": {
const matched = await this.hintRepository.getMatchedHints();
if (matched.length === 1) {
await this.activate(tabId, matched[0]);
}
return false;
}
case "Esc":
return false;
case "Backspace":
case "Delete":
await this.hintRepository.popKey();
await this.filter(tabId, await this.hintRepository.getKeys());
await this.showOnlyMatched(tabId);
return true;
}

await this.hintRepository.pushKey(key);

const prefix = await this.hintRepository.getKeys();
const matched = await this.hintRepository.getMatchedHints();
if (matched.length === 0) {
return false;
} else if (matched.length === 1) {
await this.activate(tabId, prefix);
await this.activate(tabId, matched[0]);
return false;
} else {
await this.filter(tabId, prefix);
await this.showOnlyMatched(tabId);
return true;
}
}

private async activate(tabId: number, tag: string): Promise<void> {
private async activate(tabId: number, target: HintTarget): Promise<void> {
const { newTab, background } = await this.hintRepository.getOption();
await this.hintClient.activateIfExists(tabId, tag, newTab, background);
await this.hintClient.activate(
tabId,
target.frameId,
target.element,
newTab,
background,
);
}

private async filter(tabId: number, prefix: string): Promise<void> {
await this.hintClient.filterHints(tabId, prefix);
private async showOnlyMatched(tabId: number): Promise<void> {
const hints = await this.hintRepository.getMatchedHints();
await this.hintClient.showHints(tabId, hints.map((hint) => hint.element));
}
}
30 changes: 17 additions & 13 deletions src/background/usecases/HintModeUseCase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import PropertySettings from "../settings/PropertySettings";
import TopFrameClient from "../clients/TopFrameClient";
import HintTagProducer from "./HintTagProducer";
import HintClient from "../clients/HintClient";
import HintRepository from "../repositories/HintRepository";
import HintRepository, {
type HintTarget,
} from "../repositories/HintRepository";

@injectable()
export default class HintModeUseCaes {
Expand Down Expand Up @@ -36,7 +38,7 @@ export default class HintModeUseCaes {

const viewport = await this.topFrameClient.getWindowViewport(tabId);
const hintKeys = new HintTagProducer(hintchars);
const hints = [];
const targets: HintTarget[] = [];
for (const frameId of frameIds) {
const framePos = await this.topFrameClient.getFramePosition(
tabId,
Expand All @@ -45,24 +47,26 @@ export default class HintModeUseCaes {
if (!framePos) {
continue;
}
const count = await this.hintClient.countHints(
const ids = await this.hintClient.lookupTargets(
tabId,
frameId,
viewport,
framePos,
);
const tags = hintKeys.produceN(count);
hints.push(...tags);
await this.hintClient.createHints(
tabId,
frameId,
tags,
viewport,
framePos,
);
const idTags = hintKeys.produceN(ids.length);
const idTagMap = Object.fromEntries(ids.map((id, i) => [id, idTags[i]]));
await this.hintClient.assignTags(tabId, frameId, idTagMap);

for (const [element, tag] of Object.entries(idTagMap)) {
targets.push({
frameId,
element,
tag,
});
}
}

await this.hintRepository.startHintMode({ newTab, background }, hints);
await this.hintRepository.startHintMode({ newTab, background }, targets);
}

async stop(tabId: number): Promise<void> {
Expand Down
43 changes: 24 additions & 19 deletions src/content/controllers/HintController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,45 +8,50 @@ export default class HintController {
private readonly hintUseCase: HintUseCase,

Check failure on line 8 in src/content/controllers/HintController.ts

View workflow job for this annotation

GitHub Actions / Build

Property 'hintUseCase' is declared but its value is never read.
) {}

async countHints({
async lookupTargets({

Check failure on line 11 in src/content/controllers/HintController.ts

View workflow job for this annotation

GitHub Actions / Build

All destructured elements are unused.
viewSize,
framePosition,
}: {
viewSize: { width: number; height: number };
framePosition: { x: number; y: number };
}): Promise<number> {
return this.hintUseCase.countHints(viewSize, framePosition);
}): Promise<{ elements: string[] }> {
throw new Error("not implemented");
// return this.hintUseCase.lookupTargets(viewSize, framePosition);
}

async createHints({
viewSize,
framePosition,
hints,
async assignTags({

Check failure on line 22 in src/content/controllers/HintController.ts

View workflow job for this annotation

GitHub Actions / Build

'elementTags' is declared but its value is never read.
elementTags,
}: {
viewSize: { width: number; height: number };
framePosition: { x: number; y: number };
hints: string[];
elementTags: Record<string, string>;
}): Promise<void> {
return this.hintUseCase.createHints(viewSize, framePosition, hints);
throw new Error("not implemented");
// return this.hintUseCase.assignTags(elementTags);
}

async filterHints({ prefix }: { prefix: string }): Promise<void> {
return this.hintUseCase.filterHints(prefix);
async showHints({

Check failure on line 31 in src/content/controllers/HintController.ts

View workflow job for this annotation

GitHub Actions / Build

'elements' is declared but its value is never read.
elements,
}: {
elements: string[];
}): Promise<void> {
throw new Error("not implemented");
// return this.hintUseCase.showHints(elements);
}

async remove(): Promise<void> {
return this.hintUseCase.remove();
async clearHints(): Promise<void> {
throw new Error("not implemented");
// return this.hintUseCase.clearHints();
}

async activateIfExists({
hint,
async activate({

Check failure on line 45 in src/content/controllers/HintController.ts

View workflow job for this annotation

GitHub Actions / Build

All destructured elements are unused.
element,
newTab,
background,
}: {
hint: string;
element: string;
newTab: boolean;
background: boolean;
}): Promise<void> {
return this.hintUseCase.activateIfExists(hint, newTab, background);
throw new Error("not implemented");
// return this.hintUseCase.activate(element, newTab, background);
}
}
Loading

0 comments on commit 3baf48e

Please sign in to comment.