ronantakizawa/codereview-bench huggingface dataset:
Repository: sunner/ChatALL
File: src/components/Notification/UpdateNotificationModal.vue (Vue)

Code before:
      </template>
    </v-snackbar>
  </div>
</template>
<script setup>
import { ref } from "vue";
import { compare } from 'compare-versions';
const { shell } = window.require("electron");

const versions = JSON.parse(localStorage.getItem("chatall-versions"));
const snackbar = ref(false);
if (versions?.latest && versions?.current && compare(versions.latest, versions.current, '>')) {
  if (!versions?.skip || compare(versions.latest, versions.skip, '>')) {
    snackbar.value = true;
  }
}

function skip() {
  snackbar.value = false;
  versions.skip = versions.latest;
  localStorage.setItem("chatall-versions", JSON.stringify(versions));
}

function install() {
  snackbar.value = false;
  shell.openExternal(`https://github.com/sunner/ChatALL/releases/tag/${versions.latest}`);
}
</script>
<style>
</style>

<style scoped>
:deep() span {
  /* font-size: 18px;
  line-height: 1.5; */
}

:deep() .v-btn {
  text-transform: none;
  margin: .2rem;
}

:deep() .v-snackbar__actions {
  margin: .3rem;
  align-items: center;
  justify-content: center;
  width: 100%;
}

:deep() .v-snackbar__content {
  padding: .5rem;

Review comment (suggestion):
```suggestion
  shell.openExternal(`https://github.com/sunner/ChatALL/releases/latest`);
```
How about above? It will redirect to the latest version and a little bit stabler.

Code after:
      </template>
    </v-snackbar>
  </div>
</template>
<script setup>
import { ref } from "vue";
import { compare } from 'compare-versions';
const { shell } = window.require("electron");

const versions = JSON.parse(localStorage.getItem("chatall-versions"));
const snackbar = ref(false);
if (versions?.latest && versions?.current && compare(versions.latest, versions.current, '>')) {
  if (!versions?.skip || compare(versions.latest, versions.skip, '>')) {
    snackbar.value = true;
  }
}

function skip() {
  snackbar.value = false;
  versions.skip = versions.latest;
  localStorage.setItem("chatall-versions", JSON.stringify(versions));
}

function openReleasePage() {
  snackbar.value = false;
  shell.openExternal(`https://github.com/sunner/ChatALL/releases/latest`);
}
</script>
<style scoped>
:deep() .v-btn {
  text-transform: none;
  margin: .2rem;
}

:deep() .v-snackbar__actions {
  margin: 0!important;
  justify-content: end;
  width: 100%;
}

:deep() .v-snackbar__content {
  padding: .5rem;
  padding-bottom: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  width: 100%;
  justify-content: center;
}
</style>
Repository: sunner/ChatALL
File: src/components/ShortcutGuide/shortcut.const.js (JavaScript)

Code before:
export const SHORTCUT_PROMPT_TEXTAREA = {
  elementId: "prompt-textarea",
  key: ["ctrl", "k"],
  offset: {
    top: 10,
  },
  alignHorizontallyCenter: true,
};

export const SHORTCUT_FIND = {
  elementId: "find-btn",
  key: {windows: ['ctrl', 'f'], mac: ['meta', 'f']},
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_CLEAR_MESSAGES = {
  elementId: "clear-messages-btn",
  key: ["ctrl", "e"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_SETTINGS = {
  elementId: "settings-btn",
  key: ["ctrl", ","],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_SHORTCUT_GUIDE = {

Review comment (suggestion):
The `key` is used in the shortcut guide. So, the changes might break the shortcut guide, try to click 
![image](https://github.com/sunner/ChatALL/assets/26683979/b71f7c68-ee05-4342-bab7-f61ae9744922)

Code after:
const is_mac = process.platform === "darwin";
const modifier = is_mac ? "meta" : "ctrl";
export const SHORTCUT_PROMPT_TEXTAREA = {
  elementId: "prompt-textarea",
  key: [modifier, "k"],
  offset: {
    top: 10,
  },
  alignHorizontallyCenter: true,
};

export const SHORTCUT_FIND = {
  elementId: "find-btn",
  key: [modifier, "f"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_CLEAR_MESSAGES = {
  elementId: "clear-messages-btn",
  key: [modifier, "e"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_SETTINGS = {
  elementId: "settings-btn",
  key: [modifier, ","],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};
Repository: sunner/ChatALL
File: src/components/ShortcutGuide/ShortcutGuide.vue (Vue)

Code before:
    div.classList.add("shortcut-label");
    div.style.top = `${position.top + (shortcut.offset?.top ?? 0)}px`;
    if (shortcut.alignHorizontallyCenter) {
      div.style.left = `${
        position.left + position.width / 2 + (shortcut.offset?.left ?? 0)
      }px`;
    } else {
      div.style.left = `${position.left + (shortcut.offset?.left ?? 0)}px`;
    }
    div.innerHTML = getShortcutLabelHTML(shortcut.key);
    div.style.flexDirection = shortcut.flexDirection;
    shortcutGuideContentRef.value.appendChild(div);
  });
});

function getShortcutLabelHTML(keys) {
  return keys.map(kbd).join("<span>+</span>");
}

function kbd(text) {
  return `<kbd>${capitalizeFirstLetter(text)}</kbd>`;
}

function capitalizeFirstLetter(string) {
  const s = string.replace('meta', 'cmd');
  return s.charAt(0).toUpperCase() + s.slice(1);
}

function addWindowResizeListener() {
  window.addEventListener("resize", closeShortcutGuide);
}

const toggleShortcutGuide = () => {
  if (props.open) {
    closeShortcutGuide();
  } else {
    openShortcutGuide();
  }
};

const closeShortcutGuide = () => {
  window.removeEventListener("resize", closeShortcutGuide);
  emit("update:open", false);
  emit("done");
};

const openShortcutGuide = () => {
  addWindowResizeListener();
  emit("update:open", true);
};


Review comment (suggestion):
```suggestion
  const string = string.replace('meta', 'cmd');
  return string.charAt(0).toUpperCase() + s.slice(1);
```

Code after:
    div.classList.add("shortcut-label");
    div.style.top = `${position.top + (shortcut.offset?.top ?? 0)}px`;
    if (shortcut.alignHorizontallyCenter) {
      div.style.left = `${
        position.left + position.width / 2 + (shortcut.offset?.left ?? 0)
      }px`;
    } else {
      div.style.left = `${position.left + (shortcut.offset?.left ?? 0)}px`;
    }
    div.innerHTML = getShortcutLabelHTML(shortcut.key);
    div.style.flexDirection = shortcut.flexDirection;
    shortcutGuideContentRef.value.appendChild(div);
  });
});

function getShortcutLabelHTML(keys) {
  return keys.map(kbd).join("<span>+</span>");
}

function kbd(text) {
  return `<kbd>${formatModifierText(text)}</kbd>`;
}

function formatModifierText(string) {
  string = string.replace('meta', 'cmd');
  return string.charAt(0).toUpperCase() + string.slice(1);
}

function addWindowResizeListener() {
  window.addEventListener("resize", closeShortcutGuide);
}

const toggleShortcutGuide = () => {
  if (props.open) {
    closeShortcutGuide();
  } else {
    openShortcutGuide();
  }
};

const closeShortcutGuide = () => {
  window.removeEventListener("resize", closeShortcutGuide);
  emit("update:open", false);
  emit("done");
};

const openShortcutGuide = () => {
  addWindowResizeListener();
  emit("update:open", true);
};
Repository: sunner/ChatALL
File: src/components/ShortcutGuide/shortcut.const.js (JavaScript)

Code before:
const is_mac = OSnow();
const control = is_mac ? "meta" : "ctrl";
function OSnow() {
  const agent = navigator.userAgent.toLowerCase();
  const isMac = /macintosh|mac os x/i.test(navigator.userAgent);
  if (agent.indexOf("win32") >= 0 || agent.indexOf("wow32") >= 0) {
    return false;
  } else if (agent.indexOf("win64") >= 0 || agent.indexOf("wow64") >= 0) {
    return false;
  } else if (isMac) {
    return true;
  }
}
export const SHORTCUT_PROMPT_TEXTAREA = {
  elementId: "prompt-textarea",
  key: [control, "k"],
  offset: {
    top: 10,
  },
  alignHorizontallyCenter: true,
};

export const SHORTCUT_FIND = {
  elementId: "find-btn",
  key: [control, "f"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_CLEAR_MESSAGES = {
  elementId: "clear-messages-btn",
  key: [control, "e"],
  offset: {
    top: 40,
  },
  flexDirection: "column",

Review comment (suggestion):
I can't understand why the function is named as `0Snow`.  And you can know whether the OS is mac by `process.platform === "darwin"` 
```suggestion
const is_mac = process.platform === "darwin";
const control = is_mac ? "meta" : "ctrl";
```

Code after:
const is_mac = process.platform === "darwin";
const modifier = is_mac ? "meta" : "ctrl";
export const SHORTCUT_PROMPT_TEXTAREA = {
  elementId: "prompt-textarea",
  key: [modifier, "k"],
  offset: {
    top: 10,
  },
  alignHorizontallyCenter: true,
};

export const SHORTCUT_FIND = {
  elementId: "find-btn",
  key: [modifier, "f"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_CLEAR_MESSAGES = {
  elementId: "clear-messages-btn",
  key: [modifier, "e"],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};

export const SHORTCUT_SETTINGS = {
  elementId: "settings-btn",
  key: [modifier, ","],
  offset: {
    top: 40,
  },
  flexDirection: "column",
};
Repository: sunner/ChatALL
File: src/components/ShortcutGuide/ShortcutGuide.vue (Vue)

Code before:
    const position = shortcutElement.getBoundingClientRect();
    const div = document.createElement("div");
    div.classList.add("shortcut-label");
    div.style.top = `${position.top + (shortcut.offset?.top ?? 0)}px`;
    if (shortcut.alignHorizontallyCenter) {
      div.style.left = `${
        position.left + position.width / 2 + (shortcut.offset?.left ?? 0)
      }px`;
    } else {
      div.style.left = `${position.left + (shortcut.offset?.left ?? 0)}px`;
    }
    div.innerHTML = getShortcutLabelHTML(shortcut.key);
    div.style.flexDirection = shortcut.flexDirection;
    shortcutGuideContentRef.value.appendChild(div);
  });
});

function getShortcutLabelHTML(keys) {
  return keys.map(kbd).join("<span>+</span>");
}

function kbd(text) {
  return `<kbd>${formatModifierTxt(text)}</kbd>`;
}

function formatModifierTxt(string) {
  string = string.replace('meta', 'cmd');
  return string.charAt(0).toUpperCase() + string.slice(1);
}

function addWindowResizeListener() {
  window.addEventListener("resize", closeShortcutGuide);
}

const toggleShortcutGuide = () => {
  if (props.open) {
    closeShortcutGuide();
  } else {
    openShortcutGuide();
  }
};

const closeShortcutGuide = () => {
  window.removeEventListener("resize", closeShortcutGuide);
  emit("update:open", false);
  emit("done");
};

const openShortcutGuide = () => {
  addWindowResizeListener();
  emit("update:open", true);

Review comment (suggestion):
```suggestion
function formatModifierText(string) {
```

Code after:
    const position = shortcutElement.getBoundingClientRect();
    const div = document.createElement("div");
    div.classList.add("shortcut-label");
    div.style.top = `${position.top + (shortcut.offset?.top ?? 0)}px`;
    if (shortcut.alignHorizontallyCenter) {
      div.style.left = `${
        position.left + position.width / 2 + (shortcut.offset?.left ?? 0)
      }px`;
    } else {
      div.style.left = `${position.left + (shortcut.offset?.left ?? 0)}px`;
    }
    div.innerHTML = getShortcutLabelHTML(shortcut.key);
    div.style.flexDirection = shortcut.flexDirection;
    shortcutGuideContentRef.value.appendChild(div);
  });
});

function getShortcutLabelHTML(keys) {
  return keys.map(kbd).join("<span>+</span>");
}

function kbd(text) {
  return `<kbd>${formatModifierText(text)}</kbd>`;
}

function formatModifierText(string) {
  string = string.replace('meta', 'cmd');
  return string.charAt(0).toUpperCase() + string.slice(1);
}

function addWindowResizeListener() {
  window.addEventListener("resize", closeShortcutGuide);
}

const toggleShortcutGuide = () => {
  if (props.open) {
    closeShortcutGuide();
  } else {
    openShortcutGuide();
  }
};

const closeShortcutGuide = () => {
  window.removeEventListener("resize", closeShortcutGuide);
  emit("update:open", false);
  emit("done");
};

const openShortcutGuide = () => {
  addWindowResizeListener();
  emit("update:open", true);
Repository: sunner/ChatALL
File: src/bots/Bot.js (JavaScript)

Code before:
  }

  /**
   * @param {*} context
   * @returns Nothing
   */
  setChatContext(context) {
    store.commit("setChatContext", {
      botClassname: this.getClassname(),
      context,
    });
  }

  wrapCollapsedSection(text) {
    // replace line break with <br/>
    text = text?.toString()?.replace(/[\r\n]+/g, "<br/>");
    return `<details open>
              <summary>Error</summary>
              <pre class="error">${text}</pre>
            </details>`;
  }

  getSSEDisplayError(event) {
    if (
      event?.source?.xhr?.status === 403 &&
      this.constructor._isUseCloudflareTurnstile
    ) {
      return `${i18n.global.t(
        "error.solveCAPTCHA",
      )}\n${this.getBotLoginHyperlink()}`;
    }
    return `${event?.source?.xhr?.status}\n${event?.source?.xhr?.response}`;
  }

  getBotLoginHyperlink() {
    const url = this.getLoginUrl();
    return `<a href="${url}" target="innerWindow">${url}</a>`;
  }
}


Review comment (suggestion):
Are there other clues, such as response messages, to determine whether the 403 is caused by Cloudflare? I am worried that not all 403s are due to Cloudflare, which may mislead users. Therefore, it is best to make judgments from the error message.

Code after:
  }

  /**
   * @param {*} context
   * @returns Nothing
   */
  setChatContext(context) {
    store.commit("setChatContext", {
      botClassname: this.getClassname(),
      context,
    });
  }

  wrapCollapsedSection(text) {
    // replace line break with <br/>
    text = text?.toString()?.replace(/[\r\n]+/g, "<br/>");
    return `<details open>
              <summary>Error</summary>
              <pre class="error">${text}</pre>
            </details>`;
  }

  getSSEDisplayError(event) {
    if (event?.source?.xhr?.getResponseHeader("cf-mitigated") === "challenge") {
      // if encounter Cloudflare challenge page, prompt user to open link and solve challenge
      return `${i18n.global.t(
        "error.solveChallenge",
      )}\n${this.getLoginHyperlink()}`;
    }
    return `${event?.source?.xhr?.status}\n${event?.source?.xhr?.response}`;
  }

  getLoginHyperlink() {
    const url = this.getLoginUrl();
    return `<a href="${url}" target="innerWindow">${url}</a>`;
  }
}
Repository: sunner/ChatALL
File: src/bots/Bot.js (JavaScript)

Code before:
    store.commit("setChatContext", {
      botClassname: this.getClassname(),
      context,
    });
  }

  wrapCollapsedSection(text) {
    // replace line break with <br/>
    text = text?.toString()?.replace(/[\r\n]+/g, "<br/>");
    return `<details open>
              <summary>Error</summary>
              <pre class="error">${text}</pre>
            </details>`;
  }

  getSSEDisplayError(event) {
    if (
      event?.source?.xhr?.status === 403 &&
      this.constructor._isUseCloudflareTurnstile
    ) {
      return `${i18n.global.t(
        "error.solveCAPTCHA",
      )}\n${this.getBotLoginHyperlink()}`;
    }
    return `${event?.source?.xhr?.status}\n${event?.source?.xhr?.response}`;
  }

  getBotLoginHyperlink() {
    const url = this.getLoginUrl();
    return `<a href="${url}" target="innerWindow">${url}</a>`;
  }
}


Review comment (suggestion):
```suggestion
  getLoginHyperlink() {
```

The function belongs to a bot. No need to mention `bot` again.

Code after:
    store.commit("setChatContext", {
      botClassname: this.getClassname(),
      context,
    });
  }

  wrapCollapsedSection(text) {
    // replace line break with <br/>
    text = text?.toString()?.replace(/[\r\n]+/g, "<br/>");
    return `<details open>
              <summary>Error</summary>
              <pre class="error">${text}</pre>
            </details>`;
  }

  getSSEDisplayError(event) {
    if (event?.source?.xhr?.getResponseHeader("cf-mitigated") === "challenge") {
      // if encounter Cloudflare challenge page, prompt user to open link and solve challenge
      return `${i18n.global.t(
        "error.solveChallenge",
      )}\n${this.getLoginHyperlink()}`;
    }
    return `${event?.source?.xhr?.status}\n${event?.source?.xhr?.response}`;
  }

  getLoginHyperlink() {
    const url = this.getLoginUrl();
    return `<a href="${url}" target="innerWindow">${url}</a>`;
  }
}
Repository: sunner/ChatALL
File: src/components/ChatSetting.vue (Vue)

Code before:
      :text="$t('chat.deleteAllChatHistory')"
      @click="deleteChats"
    ></v-btn>
    <v-btn
      color="primary"
      variant="outlined"
      :text="$t('chat.downloadAllChatHistory')"
      @click="downloadJson"
      style="margin-left:10px;"
    ></v-btn>

  </v-list-item>
  <ConfirmModal ref="confirmModal" />
</template>

<script setup>
import { ref } from "vue";
import { useStore } from "vuex";
import i18n from "@/i18n";
import ConfirmModal from "@/components/ConfirmModal.vue";
const emit = defineEmits(["close-dialog"]);
const confirmModal = ref();
const store = useStore();

const downloadJson = () => {
  const s = JSON.stringify(localStorage);
  const blob = new Blob([s], { type: 'application/json' })
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  const file_name = i18n.global.t('chat.downloadAllChatHistory')
  a.download = `${file_name}.json`
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  URL.revokeObjectURL(url)
}
async function deleteChats() {
  const confirm = await confirmModal.value.showModal(
    "",
    i18n.global.t("chat.confirmDeleteAllChatHistory"),
  );
  if (confirm) {
    store.commit("deleteChats");
    emit("close-dialog");
  }
}
</script>


Review comment (suggestion):
1. stringified json is string, not json. It can not be parsed correctly by JSON editor
2. Not all localStorage are history. Download messages only please. Moreover, some messages and attributes should be filtered out

Code after:
      :text="$t('chat.deleteAllChatHistory')"
      @click="deleteChats"
    ></v-btn>
    <v-btn
      color="primary"
      variant="outlined"
      :text="$t('chat.downloadAllChatHistory')"
      @click="downloadJson"
      style="margin-left: 10px"
    ></v-btn>
  </v-list-item>
  <ConfirmModal ref="confirmModal" />
</template>

<script setup>
import { ref } from "vue";
import { useStore } from "vuex";
import i18n from "@/i18n";
import ConfirmModal from "@/components/ConfirmModal.vue";
import bots from "@/bots";
const emit = defineEmits(["close-dialog"]);
const confirmModal = ref();
const store = useStore();

// This function downloads the chat history as a JSON file.
const downloadJson = () => {
  // Get the chat history from localStorage.
  const chatallMessages = localStorage.getItem("chatall-messages");
  if (!chatallMessages) {
    console.error("chatall-messages not found in localStorage");
    return;
  }

  const chats = JSON.parse(chatallMessages)?.chats ?? [];

  // Create an array of messages from the chat history.
  const messages = chats
    .filter((d) => !d.hide)
    .map((chat) => ({
      // The title of the chat.
      title: chat.title,
      // The messages in the chat.
      messages: chat.messages
        .filter((d) => !d.hide)
        .reduce((arr, message) => {
          const t = message.type;
          const content = message.content;
          if (t == "prompt") {
            arr.push({
              prompt: content,
              responses: [],
Repository: sunner/ChatALL
File: src/bots/OpenAssistantBot.js (JavaScript)

Code before:
import Bot from "@/bots/Bot";
import i18n from "@/i18n";
import AsyncLock from "async-lock";
import axios from "axios";
import queryString from "query-string";
import { SSE } from "sse.js";

export default class YouChatBot extends Bot {
  static _brandId = "openAssistant"; // Brand id of the bot, should be unique. Used in i18n.
  static _className = "OpenAssistantBot"; // Class name of the bot
  static _logoFilename = "open-assistant.svg"; // Place it in public/bots/
  static _loginUrl = "https://open-assistant.io/auth/signin/";
  static _lock = new AsyncLock(); // AsyncLock for prompt requests

  constructor() {
    super();
  }

  /**
   * Check whether the bot is logged in, settings are correct, etc.
   * @returns {boolean} - true if the bot is available, false otherwise.
   * @sideeffect - Set this.constructor._isAvailable
   */
  async checkAvailability() {
    await axios
      .get("https://open-assistant.io/api/auth/session")
      .then((response) => {
        if (response.data) {
          this.constructor._isAvailable = false;
          // eslint-disable-next-line
          for (var i in response.data) {
            // if data object not empty, user logged in
            this.constructor._isAvailable = true;

Review comment (suggestion):
```suggestion
export default class OpenAssistantBot extends Bot {
```

Code after:
import Bot from "@/bots/Bot";
import i18n from "@/i18n";
import AsyncLock from "async-lock";
import axios from "axios";
import queryString from "query-string";
import { SSE } from "sse.js";

export default class OpenAssistantBot extends Bot {
  static _brandId = "openAssistant"; // Brand id of the bot, should be unique. Used in i18n.
  static _className = "OpenAssistantBot"; // Class name of the bot
  static _logoFilename = "open-assistant.svg"; // Place it in public/bots/
  static _loginUrl = "https://open-assistant.io/auth/signin/";
  static _lock = new AsyncLock(); // AsyncLock for prompt requests

  constructor() {
    super();
  }

  /**
   * Check whether the bot is logged in, settings are correct, etc.
   * @returns {boolean} - true if the bot is available, false otherwise.
   * @sideeffect - Set this.constructor._isAvailable
   */
  async checkAvailability() {
    await axios
      .get("https://open-assistant.io/api/auth/session")
      .then((response) => {
        if (response.data) {
          this.constructor._isAvailable = false;
          // eslint-disable-next-line
          for (var i in response.data) {
            // if data object not empty, user logged in
            this.constructor._isAvailable = true;