ronantakizawa/codereview-bench huggingface dataset:
Repository: 4ian/GDevelop
File: Extensions/TextInput/textinputruntimeobject-pixi-renderer.ts (TypeScript)

Code before:
      // Display after the object is positioned.
      this._form.style.display = 'initial';
    }

    updateString() {
      if (!this._input) return;
      this._input.value = this._object.getString();
    }

    updatePlaceholder() {
      if (!this._input) return;
      this._input.placeholder = this._object.getPlaceholder();
    }

    updateFont() {
      if (!this._input) return;
      this._input.style.fontFamily = this._instanceContainer
        .getGame()
        .getFontManager()
        .getFontFamily(this._object.getFontResourceName());
    }

    updateOpacity() {
      if (!this._form) return;
      this._form.style.opacity =
        '' + (this._object.getOpacity() / 255).toFixed(3);
    }

    updateInputType() {
      if (!this._input) return;

      const isTextArea = this._input instanceof HTMLTextAreaElement;
      const shouldBeTextArea = this._object.getInputType() === 'text area';
      if (isTextArea !== shouldBeTextArea) {
        this._destroyElement();
        this._createElement();
      }

      const newType =
        userFriendlyToHtmlInputTypes[this._object.getInputType()] || 'text';
      this._input.setAttribute('type', newType);
    }

    updateTextColor() {
      if (!this._input) return;

      this._input.style.color = formatRgbAndOpacityToCssRgba(
        this._object._getRawTextColor(),
        255
      );
    }

Review comment (suggestion):
```suggestion
        (this._object.getOpacity() / 255).toFixed(3);
```
toFixed returns as string already

Code after:
      // Display after the object is positioned.
      this._form.style.display = 'initial';
    }

    updateString() {
      if (!this._input) return;
      this._input.value = this._object.getString();
    }

    updatePlaceholder() {
      if (!this._input) return;
      this._input.placeholder = this._object.getPlaceholder();
    }

    updateFont() {
      if (!this._input) return;
      this._input.style.fontFamily = this._instanceContainer
        .getGame()
        .getFontManager()
        .getFontFamily(this._object.getFontResourceName());
    }

    updateOpacity() {
      if (!this._form) return;
      this._form.style.opacity = (this._object.getOpacity() / 255).toFixed(3);
    }

    updateInputType() {
      if (!this._input) return;

      const isTextArea = this._input instanceof HTMLTextAreaElement;
      const shouldBeTextArea = this._object.getInputType() === 'text area';
      if (isTextArea !== shouldBeTextArea) {
        this._destroyElement();
        this._createElement();
      }

      const newType =
        userFriendlyToHtmlInputTypes[this._object.getInputType()] || 'text';
      this._input.setAttribute('type', newType);
    }

    updateTextColor() {
      if (!this._input) return;

      this._input.style.color = formatRgbAndOpacityToCssRgba(
        this._object._getRawTextColor(),
        255
      );
    }
Repository: 4ian/GDevelop
File: newIDE/app/src/Utils/GDevelopServices/Asset.js (JavaScript)

Code before:
|};

export type UserCourseProgress = {|
  userId: string,
  courseId: string,
  progress: {| chapterId: string, completedTasks: number[] |}[],
|};

export type Environment = 'staging' | 'live';

export const client = axios.create({
  baseURL: GDevelopAssetApi.baseUrl,
});

export const isAssetPackAudioOnly = (assetPack: PrivateAssetPack): boolean => {
  const contentKeys = Object.keys(assetPack.content);
  return contentKeys.length === 1 && contentKeys[0] === 'audio';
};
export const doesAssetPackContainAudio = (
  assetPack: PrivateAssetPack
): boolean => !!assetPack.content.audio && assetPack.content.audio > 0;

/**
 * Check if the IDE version, passed as argument, satisfy the version required by the asset.
 */
export const isCompatibleWithAsset = (
  ideVersion: string,
  assetRequiredGDevelopVersion: ?string
) =>
  assetRequiredGDevelopVersion
    ? semverSatisfies(ideVersion, assetRequiredGDevelopVersion, {
        includePrerelease: true,
      })
    : true;

export const listAllPublicAssets = async ({
  environment,
}: {|
  environment: Environment,
|}): Promise<AllPublicAssets> => {
  const response = await client.get(`/asset`, {
    params: {
      environment,
    },
  });

  const { assetShortHeadersUrl, filtersUrl, assetPacksUrl } = response.data;

  const responsesData = await Promise.all([
    client
      .get(assetShortHeadersUrl)

Review comment (suggestion):
```suggestion
export const isCompatibleWithGDevelopVersion = (
```

Code after:
|};

export type UserCourseProgress = {|
  userId: string,
  courseId: string,
  progress: {| chapterId: string, completedTasks: number[] |}[],
|};

export type Environment = 'staging' | 'live';

export const client = axios.create({
  baseURL: GDevelopAssetApi.baseUrl,
});

export const isAssetPackAudioOnly = (assetPack: PrivateAssetPack): boolean => {
  const contentKeys = Object.keys(assetPack.content);
  return contentKeys.length === 1 && contentKeys[0] === 'audio';
};
export const doesAssetPackContainAudio = (
  assetPack: PrivateAssetPack
): boolean => !!assetPack.content.audio && assetPack.content.audio > 0;

/**
 * Check if the IDE version, passed as argument, satisfy the version required by the asset.
 */
export const isCompatibleWithGDevelopVersion = (
  ideVersion: string,
  assetRequiredGDevelopVersion: ?string
) =>
  assetRequiredGDevelopVersion
    ? semverSatisfies(ideVersion, assetRequiredGDevelopVersion, {
        includePrerelease: true,
      })
    : true;

export const listAllPublicAssets = async ({
  environment,
}: {|
  environment: Environment,
|}): Promise<AllPublicAssets> => {
  const response = await client.get(`/asset`, {
    params: {
      environment,
    },
  });

  const { assetShortHeadersUrl, filtersUrl, assetPacksUrl } = response.data;

  const responsesData = await Promise.all([
    client
      .get(assetShortHeadersUrl)
Repository: 4ian/GDevelop
File: newIDE/app/src/MainFrame/index.js (JavaScript)

Code before:
        ...state,
        currentFileMetadata: fileMetadata,
      }));
    },
    ensureResourcesAreMoved,
    onGameRegistered: gamesList.fetchGames,
  });

  const {
    onSelectExampleShortHeader,
    onSelectPrivateGameTemplateListingData,
    renderNewProjectDialog,
    fetchAndOpenNewProjectSetupDialogForExample,
    openNewProjectDialog,
  } = useNewProjectDialog({
    isProjectOpening,
    newProjectSetupDialogOpen,
    setNewProjectSetupDialogOpen,
    createEmptyProject,
    createProjectFromExample,
    createProjectFromPrivateGameTemplate,
    createProjectFromAIGeneration,
    storageProviders: props.storageProviders,
  });

  const gamesPlatformFrameTools = UseGamesPlatformFrame({
    fetchAndOpenNewProjectSetupDialogForExample,
    onOpenProfileDialog: () => openProfileDialog(true),
  });

  const closeApp = React.useCallback((): void => {
    return Window.quit();
  }, []);

  const toggleProjectManager = React.useCallback(
    () => {
      if (toolbar.current)
        openProjectManager(projectManagerOpen => !projectManagerOpen);
    },
    [openProjectManager]
  );

  const setEditorToolbar = (editorToolbar: any, isCurrentTab = true) => {
    if (!toolbar.current || !isCurrentTab) return;

    toolbar.current.setEditorToolbar(editorToolbar);
  };

  const onInstallExtension = (extensionShortHeader: ExtensionShortHeader) => {
    const { currentProject } = state;
    if (!currentProject) return;

Review comment (suggestion):
```suggestion
  const gamesPlatformFrameTools = useGamesPlatformFrame({
```

Code after:
        ...state,
        currentFileMetadata: fileMetadata,
      }));
    },
    ensureResourcesAreMoved,
    onGameRegistered: gamesList.fetchGames,
  });

  const {
    onSelectExampleShortHeader,
    onSelectPrivateGameTemplateListingData,
    renderNewProjectDialog,
    fetchAndOpenNewProjectSetupDialogForExample,
    openNewProjectDialog,
  } = useNewProjectDialog({
    isProjectOpening,
    newProjectSetupDialogOpen,
    setNewProjectSetupDialogOpen,
    createEmptyProject,
    createProjectFromExample,
    createProjectFromPrivateGameTemplate,
    createProjectFromAIGeneration,
    storageProviders: props.storageProviders,
  });

  const onOpenProfileDialog = React.useCallback(
    () => {
      openProfileDialog(true);
    },
    [openProfileDialog]
  );

  const gamesPlatformFrameTools = useGamesPlatformFrame({
    fetchAndOpenNewProjectSetupDialogForExample,
    onOpenProfileDialog,
  });

  const closeApp = React.useCallback((): void => {
    return Window.quit();
  }, []);

  const toggleProjectManager = React.useCallback(
    () => {
      if (toolbar.current)
        openProjectManager(projectManagerOpen => !projectManagerOpen);
    },
    [openProjectManager]
  );

  const setEditorToolbar = (editorToolbar: any, isCurrentTab = true) => {
    if (!toolbar.current || !isCurrentTab) return;
Repository: 4ian/GDevelop
File: GDJS/Runtime/ResourceManager.ts (TypeScript)

Code before:
    /**
     * Process the specified resource.
     *
     * This method will only be run while loading screen is shown. It can do
     * heavy tasks like parsing data.
     */
    processResource(resourceName: string): Promise<void>;

    /**
     * Return the kind of resources handled by this manager.
     */
    getResourceKinds(): Array<ResourceKind>;

    /**
     * Clear all resources, data, loaders stored by this manager.
     * Using the manager after calling this method is undefined behavior.
     */
    dispose(): void;

    /**
     * Clear any data in cache for a resource. Embedded resources are also
     * cleared.
     *
     * Usually called when scene resources are unloaded.
     *
     * @param resourceData The resources that need to be cleared
     */
    unloadResource(resourceData: ResourceData): void;
  }
}


Review comment (suggestion):
```suggestion
     * @param resourceData The resource to clear
```

Code after:
    /**
     * Process the specified resource.
     *
     * This method will only be run while loading screen is shown. It can do
     * heavy tasks like parsing data.
     */
    processResource(resourceName: string): Promise<void>;

    /**
     * Return the kind of resources handled by this manager.
     */
    getResourceKinds(): Array<ResourceKind>;

    /**
     * Clear all resources, data, loaders stored by this manager.
     * Using the manager after calling this method is undefined behavior.
     */
    dispose(): void;

    /**
     * Clear any data in cache for a resource. Embedded resources are also
     * cleared.
     *
     * Usually called when scene resources are unloaded.
     *
     * @param resourceData The resource to clear
     */
    unloadResource(resourceData: ResourceData): void;
  }
}
Repository: 4ian/GDevelop
File: newIDE/app/src/ChoicesEditor/index.js (JavaScript)

Code before:
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import * as React from 'react';
import {
  ResponsiveLineStackLayout,
  LineStackLayout,
  ColumnStackLayout,
} from '../UI/Layout';
import { Line } from '../UI/Grid';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import { IconButton } from '@material-ui/core';
import RaisedButton from '../UI/RaisedButton';
import Add from '../UI/CustomSvgIcons/Add';
import Trash from '../UI/CustomSvgIcons/Trash';

export type Choice = { value: string, label: string };

type ChoicesEditorProps = {|
  choices: Array<Choice>,
  setChoices: (Array<Choice>) => void,
  disabled?: boolean,
  hideLabels?: boolean,
|};

const ChoicesEditor = ({
  choices,
  setChoices,
  disabled,
  hideLabels,
}: ChoicesEditorProps) => {
  const updateChoices = () => setChoices(choices);

  return (
    <ResponsiveLineStackLayout>
      <ColumnStackLayout justifyContent="flex-end" expand noMargin>
        {choices.map((item, index) => (
          <LineStackLayout
            key={index}
            justifyContent="flex-end"
            expand
            alignItems="center"

Review comment (suggestion):
```suggestion
export type Choice = {| 
  value: string, 
  label: string,
|};
```

Code after:
// @flow
import { Trans } from '@lingui/macro';
import { t } from '@lingui/macro';
import * as React from 'react';
import {
  ResponsiveLineStackLayout,
  LineStackLayout,
  ColumnStackLayout,
} from '../UI/Layout';
import { Line } from '../UI/Grid';
import SemiControlledTextField from '../UI/SemiControlledTextField';
import { IconButton } from '@material-ui/core';
import RaisedButton from '../UI/RaisedButton';
import Add from '../UI/CustomSvgIcons/Add';
import Trash from '../UI/CustomSvgIcons/Trash';

export type Choice = {|
  value: string,
  label: string,
|};

type ChoicesEditorProps = {|
  choices: Array<Choice>,
  setChoices: (Array<Choice>) => void,
  disabled?: boolean,
  hideLabels?: boolean,
|};

const ChoicesEditor = ({
  choices,
  setChoices,
  disabled,
  hideLabels,
}: ChoicesEditorProps) => {
  const updateChoices = () => setChoices(choices);

  return (
    <ResponsiveLineStackLayout>
      <ColumnStackLayout justifyContent="flex-end" expand noMargin>
        {choices.map((item, index) => (
          <LineStackLayout
            key={index}
Repository: 4ian/GDevelop
File: GDJS/Runtime/pixi-renderers/pixi-bitmapfont-manager.ts (TypeScript)

Code before:
        PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
      }

      this._pixiBitmapFontsInUse = {};
      this._pixiBitmapFontsToUninstall.length = 0;
      this._loadedFontsData.clear();
    }

    unloadResource(resourceData: ResourceData): void {
      this._loadedFontsData.delete(resourceData);

      for (const bitmapFontInstallKey in this._pixiBitmapFontsInUse) {
        if (bitmapFontInstallKey.startsWith(resourceData.name)) {
          PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
          delete this._pixiBitmapFontsInUse[bitmapFontInstallKey];
        }
      }

      for (
        let index = 0;
        index < this._pixiBitmapFontsToUninstall.length;
        index++
      ) {
        const bitmapFontInstallKey = this._pixiBitmapFontsToUninstall[index];

        if (bitmapFontInstallKey.startsWith(resourceData.name)) {
          PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
          this._pixiBitmapFontsToUninstall.splice(index, 1);
          index--;
        }
      }
    }
  }

  // Register the class to let the engine use it.
  export const BitmapFontManager = gdjs.PixiBitmapFontManager;
  export type BitmapFontManager = gdjs.PixiBitmapFontManager;
}


Review comment (suggestion):
Same, probably safer to add the delimiter:
```suggestion
        if (bitmapFontInstallKey.startsWith(resourceData.name + '@')) {
```

Code after:
        PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
      }

      this._pixiBitmapFontsInUse = {};
      this._pixiBitmapFontsToUninstall.length = 0;
      this._loadedFontsData.clear();
    }

    unloadResource(resourceData: ResourceData): void {
      this._loadedFontsData.delete(resourceData);

      for (const bitmapFontInstallKey in this._pixiBitmapFontsInUse) {
        if (bitmapFontInstallKey.startsWith(resourceData.name + '@')) {
          PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
          delete this._pixiBitmapFontsInUse[bitmapFontInstallKey];
        }
      }

      for (
        let index = 0;
        index < this._pixiBitmapFontsToUninstall.length;
        index++
      ) {
        const bitmapFontInstallKey = this._pixiBitmapFontsToUninstall[index];

        if (bitmapFontInstallKey.startsWith(resourceData.name + '@')) {
          PIXI.BitmapFont.uninstall(bitmapFontInstallKey);
          this._pixiBitmapFontsToUninstall.splice(index, 1);
          index--;
        }
      }
    }
  }

  // Register the class to let the engine use it.
  export const BitmapFontManager = gdjs.PixiBitmapFontManager;
  export type BitmapFontManager = gdjs.PixiBitmapFontManager;
}
Repository: 4ian/GDevelop
File: newIDE/app/src/Utils/ProjectSettingsReader.js (JavaScript)

Code before:
// @flow
import optionalRequire from './OptionalRequire';
import YAML from 'yaml';

const fs = optionalRequire('node:fs');
const fsPromises = fs ? fs.promises : null;
const path = optionalRequire('node:path');

export type RawProjectSettings = {
  preferences?: { [string]: mixed },
};

export type ParsedProjectSettings = {
  preferences: { [string]: boolean | string | number },
};

const SETTINGS_FILE_NAME = 'settings.yaml';

/**
 * Gets the directory path from a project file path.
 */
const getProjectDirectory = (projectFilePath: string): string | null => {
  if (!path) return null;
  return path.dirname(projectFilePath);
};

/**
 * Reads and parses the settings.yaml file from a project's root directory.
 * Returns null if:
 * - The file doesn't exist
 * - The file system is not available (running in browser)
 * - The project path is not a local file path
 *
 * @param projectFilePath - The full path to the project's main JSON file
 * @returns Parsed project settings or null
 */
export const readProjectSettings = async (
  projectFilePath: string
): Promise<ParsedProjectSettings | null> => {
  if (!fsPromises || !path) {
    return null;
  }

Review comment (suggestion):
I suggest we use a slightly more specific name to avoid conflicts:
```suggestion
const SETTINGS_FILE_NAME = 'gdevelop-settings.yaml';
```

Code after:
// @flow
import optionalRequire from './OptionalRequire';
import YAML from 'yaml';
import { SafeExtractor } from './SafeExtractor';

const fs = optionalRequire('fs');
const fsPromises = fs ? fs.promises : null;
const path = optionalRequire('path');

export type ParsedProjectSettings = {
  preferences: { [string]: boolean | string | number },
};

const SETTINGS_FILE_NAME = 'gdevelop-settings.yaml';

const getProjectDirectory = (projectFilePath: string): string | null => {
  if (!path) return null;
  return path.dirname(projectFilePath);
};

/**
 * Reads and parses the gdevelop-settings.yaml file from a project's root directory.
 * Returns null if:
 * - The file doesn't exist
 * - The file system is not available (running in browser)
 * - The project path is not a local file path
 */
export const readProjectSettings = async (
  projectFilePath: string
): Promise<ParsedProjectSettings | null> => {
  if (!fsPromises || !path) {
    return null;
  }

  const projectDirectory = getProjectDirectory(projectFilePath);
  if (!projectDirectory) {
    return null;
  }

  const settingsFilePath = path.join(projectDirectory, SETTINGS_FILE_NAME);

  try {
Repository: 4ian/GDevelop
File: newIDE/app/src/ResourcesList/ResourcePreview/AudioPreview.js (JavaScript)

Code before:
// @flow
import * as React from 'react';
import SoundPlayer, { type SoundPlayerInterface } from '../../UI/SoundPlayer';
import GenericIconPreview from './GenericIconPreview';
import Music from '../../UI/CustomSvgIcons/Music';

type Props = {|
  resourceName: string,
  audioResourceSource: string,
|};

const AudioPreview = ({ resourceName, audioResourceSource }: Props) => {
  const soundPlayerRef = React.useRef<?SoundPlayerInterface>(null);
  const [hasError, setHasError] = React.useState(false);

  const onSoundLoaded = React.useCallback(() => {
    // Sound loaded callback
    setHasError(false);
  }, []);

  const onSoundError = React.useCallback(() => {
    setHasError(true);
  }, []);

  if (hasError) {
    return <GenericIconPreview renderIcon={props => <Music {...props} />} />;
  }

  return (
    <SoundPlayer
      ref={soundPlayerRef}
      soundSrc={audioResourceSource || null}
      onSoundLoaded={onSoundLoaded}
      onSoundError={onSoundError}
      title={resourceName}
      subtitle={null}
    />
  );

Review comment (suggestion):
This ref is unused -> remove it.
```suggestion
```

Code after:
// @flow
import * as React from 'react';
import SoundPlayer from '../../UI/SoundPlayer';
import GenericIconPreview from './GenericIconPreview';
import Music from '../../UI/CustomSvgIcons/Music';

type Props = {|
  resourceName: string,
  audioResourceSource: string,
|};

const AudioPreview = ({ resourceName, audioResourceSource }: Props) => {
  const [hasError, setHasError] = React.useState(false);

  const onSoundLoaded = React.useCallback(() => {
    setHasError(false);
  }, []);

  const onSoundError = React.useCallback(() => {
    setHasError(true);
  }, []);

  if (hasError) {
    return <GenericIconPreview renderIcon={props => <Music {...props} />} />;
  }

  return (
    <SoundPlayer
      soundSrc={audioResourceSource || null}
      onSoundLoaded={onSoundLoaded}
      onSoundError={onSoundError}
      title={resourceName}
      subtitle={null}
    />
  );
};

export default AudioPreview;
Repository: 4ian/GDevelop
File: newIDE/app/src/UI/SoundPlayer/CompactSoundPlayer.js (JavaScript)

Code before:
    const mobileAudioRef = React.useRef<?Audio>(null);
    const [isPlaying, setIsPlaying] = React.useState(false);

    const onPlayPause = React.useCallback(
      (forcePlay?: boolean) => {
        if (!soundSrc) return;
        setIsPlaying(_isPlaying => forcePlay || !_isPlaying);
      },
      [soundSrc]
    );

    const onLoad = React.useCallback(() => {
      setIsPlaying(false);
    }, []);

    const onAudioReady = React.useCallback(
      () => {
        if (!mobileAudioRef.current) return;
        if (onSoundLoaded) onSoundLoaded();
      },
      [onSoundLoaded]
    );

    React.useImperativeHandle(ref, () => ({
      playPause: onPlayPause,
    }));

    React.useEffect(
      () => {
        if (!mobileAudioRef.current) return;
        if (isPlaying) {
          mobileAudioRef.current.play();
        } else {
          mobileAudioRef.current.pause();
        }
      },
      [isPlaying]
    );

    React.useEffect(
      () => {
        if (soundSrc) {
          if (mobileAudioRef.current) {
            mobileAudioRef.current.pause();
          }
          const audio = new Audio(soundSrc);
          audio.loop = true;
          audio.addEventListener('loadstart', onLoad);
          audio.addEventListener('loadedmetadata', onAudioReady);
          audio.addEventListener('error', () => {
            if (onSoundError) onSoundError();

Review comment (suggestion):
You only use onPlayPause in this file. No reason to expose it to the outside world.

```suggestion
```

Code after:
}: Props) => {
  const audioRef = React.useRef<?HTMLAudioElement>(null);
  const [isPlaying, setIsPlaying] = React.useState(false);
  const [isLoading, setIsLoading] = React.useState(false);
  const [isAudioLoaded, setIsAudioLoaded] = React.useState(false);
  const isMountedRef = useIsMounted();
  const abortControllerRef = React.useRef<?AbortController>(null);

  const cleanupAudioAndListeners = React.useCallback(() => {
    const audio = audioRef.current;
    if (!audio) return;

    audio.pause();
    if (abortControllerRef.current) abortControllerRef.current.abort();

    audio.src = '';
    audioRef.current = null;
    abortControllerRef.current = null;
    setIsPlaying(false);
    setIsLoading(false);
    setIsAudioLoaded(false);
  }, []);

  const initializeAudio = React.useCallback(
    () => {
      if (!soundSrc || audioRef.current) return;

      setIsLoading(true);
      const audio = new Audio(soundSrc);
      audio.loop = false;

      const abortController = new AbortController();
      abortControllerRef.current = abortController;

      const handleLoadedMetadata = () => {
        if (!isMountedRef.current) return;
        setIsLoading(false);
        setIsAudioLoaded(true);
        if (onSoundLoaded) onSoundLoaded();
      };

      const handleError = () => {
        if (!isMountedRef.current) return;
        setIsLoading(false);
        setIsPlaying(false);
        setIsAudioLoaded(false);
        if (onSoundError) onSoundError();
      };

      const handleEnded = () => {
        if (!isMountedRef.current) return;