mirror of
https://github.com/XFox111/PasswordGeneratorExtension.git
synced 2026-04-22 08:08:01 +03:00
Minor 5.1.0 (#560)
* Force no-wrap for special characters tooltip (resolves #551) (#555) * Increase width of password count field in advanced generator (resolves #548) (#554) * Added password length limit in advanced generator (#552) * Added password length limit in advanced generator (resolves #547) * Add radix to parseInt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove unnecessary callback dependency Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * npm audit fix (#556) * Added length slider range limits (#557) * Added length slider range limits (resolves #546) * Fix validateMaxLimit logic Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Added password length update on input field blur (resolves #549) (#559) * Separator option for advanced password generator (#558) * Added separator option for advanced password generator (resolves #545) * Fix typos * Fixed merge typo * Update package.json * Bump @fluentui/react-components from 9.64.0 to 9.66.5 (#561) Bumps [@fluentui/react-components](https://github.com/microsoft/fluentui) from 9.64.0 to 9.66.5. - [Release notes](https://github.com/microsoft/fluentui/releases) - [Changelog](https://github.com/microsoft/fluentui/blob/master/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/fluentui/compare/@fluentui/react-components_v9.64.0...@fluentui/react-components_v9.66.5) --- updated-dependencies: - dependency-name: "@fluentui/react-components" dependency-version: 9.66.5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @typescript-eslint/parser from 8.33.1 to 8.35.1 (#562) Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.33.1 to 8.35.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/parser) --- updated-dependencies: - dependency-name: "@typescript-eslint/parser" dependency-version: 8.35.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @eslint/js from 9.28.0 to 9.30.0 (#564) Bumps [@eslint/js](https://github.com/eslint/eslint/tree/HEAD/packages/js) from 9.28.0 to 9.30.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/commits/v9.30.0/packages/js) --- updated-dependencies: - dependency-name: "@eslint/js" dependency-version: 9.30.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump eslint from 9.28.0 to 9.30.0 (#566) Bumps [eslint](https://github.com/eslint/eslint) from 9.28.0 to 9.30.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v9.28.0...v9.30.0) --- updated-dependencies: - dependency-name: eslint dependency-version: 9.30.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @typescript-eslint/eslint-plugin from 8.33.1 to 8.35.1 (#563) Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.33.1 to 8.35.1. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.35.1/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-version: 8.35.1 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Bump @fluentui/react-icons from 2.0.302 to 2.0.305 (#565) Bumps [@fluentui/react-icons](https://github.com/microsoft/fluentui-system-icons) from 2.0.302 to 2.0.305. - [Changelog](https://github.com/microsoft/fluentui-system-icons/blob/main/fluentui-android-system-icons-release.yml) - [Commits](https://github.com/microsoft/fluentui-system-icons/commits) --- updated-dependencies: - dependency-name: "@fluentui/react-icons" dependency-version: 2.0.305 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Moved default password lengths to constants (#553) * Added radix 10 for parseInt (#553) * Removed ts-expect-error microsoft/fluentui#27090 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -17,6 +17,6 @@ export const useStyles = makeStyles({
|
|||||||
{
|
{
|
||||||
display: "grid",
|
display: "grid",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
gridTemplateColumns: "24px 24px 56px",
|
gridTemplateColumns: "24px 24px 60px",
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export default function GeneratorForm(props: GeneratorFormProps): ReactElement
|
|||||||
|
|
||||||
const setPasswordCount = useCallback((_: any, e: InputOnChangeData) =>
|
const setPasswordCount = useCallback((_: any, e: InputOnChangeData) =>
|
||||||
{
|
{
|
||||||
const n = parseInt(e.value ?? "1");
|
const n = parseInt(e.value ?? "1", 10);
|
||||||
private_setPasswordCount(isNaN(n) || n < 1 ? null : Math.min(n, 1000));
|
private_setPasswordCount(isNaN(n) || n < 1 ? null : Math.min(n, 1000));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function PassphraseSection(props: GeneratorProps): ReactElement
|
|||||||
|
|
||||||
const setWordCount = useCallback((_: any, e: InputOnChangeData) =>
|
const setWordCount = useCallback((_: any, e: InputOnChangeData) =>
|
||||||
{
|
{
|
||||||
const n = parseInt(e.value ?? "");
|
const n = parseInt(e.value ?? "", 10);
|
||||||
private_setWordCount(isNaN(n) || n < 1 ? null : Math.min(n, 100));
|
private_setWordCount(isNaN(n) || n < 1 ? null : Math.min(n, 100));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ import * as fui from "@fluentui/react-components";
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import { GeneratorProps } from "../Page";
|
import { GeneratorProps } from "../Page";
|
||||||
import GeneratorForm from "../components/GeneratorForm";
|
import GeneratorForm from "../components/GeneratorForm";
|
||||||
|
import { DEFAULT_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } from "@/utils/constants";
|
||||||
|
|
||||||
// TODO: needs refactoring
|
// TODO: needs refactoring
|
||||||
export default function PasswordSection(props: GeneratorProps): ReactElement
|
export default function PasswordSection(props: GeneratorProps): ReactElement
|
||||||
{
|
{
|
||||||
const [state, private_setState] = useState<PasswordSectionState>({
|
const [state, private_setState] = useState<PasswordSectionState>({
|
||||||
length: 8,
|
length: DEFAULT_PASSWORD_LENGTH,
|
||||||
enableUppercase: true, uppercaseCount: 1,
|
enableUppercase: true, uppercaseCount: 1,
|
||||||
enableLowercase: true, lowercaseCount: 1,
|
enableLowercase: true, lowercaseCount: 1,
|
||||||
enableNumeric: true, numericCount: 1,
|
enableNumeric: true, numericCount: 1,
|
||||||
@@ -19,7 +20,11 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
excludeSimilar: true,
|
excludeSimilar: true,
|
||||||
excludeAmbiguous: true,
|
excludeAmbiguous: true,
|
||||||
excludeRepeating: false,
|
excludeRepeating: false,
|
||||||
excludeCustom: false, excludeCustomSet: ""
|
excludeCustom: false, excludeCustomSet: "",
|
||||||
|
|
||||||
|
enableSeparator: false,
|
||||||
|
separator: "-",
|
||||||
|
separatorInterval: DEFAULT_PASSWORD_LENGTH / 2
|
||||||
});
|
});
|
||||||
|
|
||||||
const cls = useStyles();
|
const cls = useStyles();
|
||||||
@@ -31,9 +36,9 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
|
|
||||||
const setLength = useCallback((_: any, e: fui.InputOnChangeData) =>
|
const setLength = useCallback((_: any, e: fui.InputOnChangeData) =>
|
||||||
{
|
{
|
||||||
const n = parseInt(e.value ?? "");
|
const n = parseInt(e.value ?? "", 10);
|
||||||
setState({ length: isNaN(n) || n < 1 ? null : n });
|
setState({ length: isNaN(n) || n < 1 ? null : Math.min(n, MAX_PASSWORD_LENGTH) });
|
||||||
}, [state]);
|
}, [setState]);
|
||||||
|
|
||||||
const saveConfiguration = useCallback(
|
const saveConfiguration = useCallback(
|
||||||
async () => await browser.storage.sync.set({ AdvancedPasswordOptions: state }),
|
async () => await browser.storage.sync.set({ AdvancedPasswordOptions: state }),
|
||||||
@@ -46,7 +51,7 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
|
|
||||||
for (let i = 0; i < count; i++)
|
for (let i = 0; i < count; i++)
|
||||||
passwords.push(generatePassword({
|
passwords.push(generatePassword({
|
||||||
length: state.length ?? 8,
|
length: state.length ?? DEFAULT_PASSWORD_LENGTH,
|
||||||
custom: state.enableCustom ? state.customCount ?? 1 : 0,
|
custom: state.enableCustom ? state.customCount ?? 1 : 0,
|
||||||
customSet: state.customSet,
|
customSet: state.customSet,
|
||||||
numeric: state.enableNumeric ? state.numericCount ?? 1 : 0,
|
numeric: state.enableNumeric ? state.numericCount ?? 1 : 0,
|
||||||
@@ -57,6 +62,8 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
excludeCustom: state.excludeCustom ? state.excludeCustomSet : "",
|
excludeCustom: state.excludeCustom ? state.excludeCustomSet : "",
|
||||||
excludeRepeating: state.excludeRepeating,
|
excludeRepeating: state.excludeRepeating,
|
||||||
excludeSimilar: state.excludeSimilar,
|
excludeSimilar: state.excludeSimilar,
|
||||||
|
separator: state.enableSeparator ? state.separator : undefined,
|
||||||
|
separatorInterval: state.separatorInterval ?? (DEFAULT_PASSWORD_LENGTH / 2)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
props.onGenerated(passwords);
|
props.onGenerated(passwords);
|
||||||
@@ -80,10 +87,38 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
onChange: (_, e) => setState({ [key]: parseCount(e.value) })
|
onChange: (_, e) => setState({ [key]: parseCount(e.value) })
|
||||||
}), [state]);
|
}), [state]);
|
||||||
|
|
||||||
|
const setSeparatorInterval = (_: any, e: fui.InputOnChangeData) =>
|
||||||
|
{
|
||||||
|
if (!e.value)
|
||||||
|
{
|
||||||
|
setState({ separatorInterval: undefined });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = parseInt(e.value, 10);
|
||||||
|
|
||||||
|
if (!isNaN(n))
|
||||||
|
setState({ separatorInterval: n < 1 ? 1 : Math.min(n, state.length ?? DEFAULT_PASSWORD_LENGTH) });
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateLength = (): void =>
|
||||||
|
{
|
||||||
|
const minLength = Math.max(MIN_PASSWORD_LENGTH,
|
||||||
|
(state.enableCustom ? state.customCount ?? 1 : 0) +
|
||||||
|
(state.enableNumeric ? state.numericCount ?? 1 : 0) +
|
||||||
|
(state.enableSpecial ? state.specialCount ?? 1 : 0) +
|
||||||
|
(state.enableUppercase ? state.uppercaseCount ?? 1 : 0) +
|
||||||
|
(state.enableLowercase ? state.lowercaseCount ?? 1 : 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!state.length || state.length < minLength)
|
||||||
|
setState({ length: minLength });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GeneratorForm onGenerate={ generate } onSave={ saveConfiguration }>
|
<GeneratorForm onGenerate={ generate } onSave={ saveConfiguration }>
|
||||||
<fui.Field label={ i18n.t("advanced.password.length") }>
|
<fui.Field label={ i18n.t("advanced.password.length") }>
|
||||||
<fui.Input value={ state.length?.toString() ?? "" } onChange={ setLength } />
|
<fui.Input value={ state.length?.toString() ?? "" } onChange={ setLength } onBlur={ updateLength } />
|
||||||
</fui.Field>
|
</fui.Field>
|
||||||
<fui.Table size="small" as="div">
|
<fui.Table size="small" as="div">
|
||||||
<fui.TableHeader as="div">
|
<fui.TableHeader as="div">
|
||||||
@@ -95,19 +130,19 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
<fui.TableBody as="div">
|
<fui.TableBody as="div">
|
||||||
<Row>
|
<Row>
|
||||||
<fui.Checkbox label={ i18n.t("common.characters.uppercase") } { ...checkboxControls("enableUppercase") } />
|
<fui.Checkbox label={ i18n.t("common.characters.uppercase") } { ...checkboxControls("enableUppercase") } />
|
||||||
<fui.Input { ...minInputControls("enableUppercase", "uppercaseCount") } />
|
<fui.Input { ...minInputControls("enableUppercase", "uppercaseCount") } onBlur={ updateLength } />
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<fui.Checkbox label={ i18n.t("common.characters.lowercase") } { ...checkboxControls("enableLowercase") } />
|
<fui.Checkbox label={ i18n.t("common.characters.lowercase") } { ...checkboxControls("enableLowercase") } />
|
||||||
<fui.Input { ...minInputControls("enableLowercase", "lowercaseCount") } />
|
<fui.Input { ...minInputControls("enableLowercase", "lowercaseCount") } onBlur={ updateLength } />
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<fui.Checkbox label={ i18n.t("common.characters.numeric") } { ...checkboxControls("enableNumeric") } />
|
<fui.Checkbox label={ i18n.t("common.characters.numeric") } { ...checkboxControls("enableNumeric") } />
|
||||||
<fui.Input { ...minInputControls("enableNumeric", "numericCount") } />
|
<fui.Input { ...minInputControls("enableNumeric", "numericCount") } onBlur={ updateLength } />
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<fui.Checkbox label={ infoLabel(i18n.t("common.characters.special"), CharacterHints.special) } { ...checkboxControls("enableSpecial") } />
|
<fui.Checkbox label={ infoLabel(i18n.t("common.characters.special"), CharacterHints.special, true) } { ...checkboxControls("enableSpecial") } />
|
||||||
<fui.Input { ...minInputControls("enableSpecial", "specialCount") } />
|
<fui.Input { ...minInputControls("enableSpecial", "specialCount") } onBlur={ updateLength } />
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
<>
|
<>
|
||||||
@@ -116,7 +151,7 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
placeholder={ i18n.t("common.characters.custom") }
|
placeholder={ i18n.t("common.characters.custom") }
|
||||||
value={ state.customSet } onChange={ (_, e) => setState({ customSet: e.value }) } />
|
value={ state.customSet } onChange={ (_, e) => setState({ customSet: e.value }) } />
|
||||||
</>
|
</>
|
||||||
<fui.Input { ...minInputControls("enableCustom", "customCount") } />
|
<fui.Input { ...minInputControls("enableCustom", "customCount") } onBlur={ updateLength } />
|
||||||
</Row>
|
</Row>
|
||||||
</fui.TableBody>
|
</fui.TableBody>
|
||||||
</fui.Table>
|
</fui.Table>
|
||||||
@@ -133,13 +168,34 @@ export default function PasswordSection(props: GeneratorProps): ReactElement
|
|||||||
value={ state.excludeCustomSet } onChange={ (_, e) => setState({ excludeCustomSet: e.value }) } />
|
value={ state.excludeCustomSet } onChange={ (_, e) => setState({ excludeCustomSet: e.value }) } />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<fui.Checkbox
|
||||||
|
{ ...checkboxControls("enableSeparator") }
|
||||||
|
label={
|
||||||
|
<span className={ cls.separatorLabel }>
|
||||||
|
{ i18n.t("advanced.password.separator1") }
|
||||||
|
<fui.Input size="small" className={ cls.separatorInput }
|
||||||
|
disabled={ !state.enableSeparator }
|
||||||
|
value={ state.separator ?? "" }
|
||||||
|
onChange={ (_, e) => setState({ separator: e.value ? e.value[e.value.length - 1] : undefined }) } />
|
||||||
|
{ i18n.t("advanced.password.separator2") }
|
||||||
|
<fui.Input size="small" className={ cls.separatorInput }
|
||||||
|
disabled={ !state.enableSeparator }
|
||||||
|
value={ state.separatorInterval?.toString() ?? "" }
|
||||||
|
onBlur={ () => state.separatorInterval ? null : setState({ separatorInterval: DEFAULT_PASSWORD_LENGTH / 2 }) }
|
||||||
|
onChange={ setSeparatorInterval } />
|
||||||
|
{ i18n.t("advanced.password.separator3") }
|
||||||
|
</span>
|
||||||
|
} />
|
||||||
|
</div>
|
||||||
</GeneratorForm>
|
</GeneratorForm>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseCount(value: string): number | null
|
function parseCount(value: string): number | null
|
||||||
{
|
{
|
||||||
const n = parseInt(value);
|
const n = parseInt(value, 10);
|
||||||
return isNaN(n) || n < 1 ? null : Math.min(n, 100);
|
return isNaN(n) || n < 1 ? null : Math.min(n, 100);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -162,6 +218,17 @@ const useStyles = fui.makeStyles({
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
},
|
},
|
||||||
|
separatorLabel:
|
||||||
|
{
|
||||||
|
display: "inline-flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: `${fui.tokens.spacingVerticalXXS} ${fui.tokens.spacingHorizontalS}`,
|
||||||
|
},
|
||||||
|
separatorInput:
|
||||||
|
{
|
||||||
|
width: "4em",
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
type PasswordSectionState =
|
type PasswordSectionState =
|
||||||
@@ -185,4 +252,8 @@ type PasswordSectionState =
|
|||||||
|
|
||||||
excludeCustomSet: string;
|
excludeCustomSet: string;
|
||||||
customSet: string;
|
customSet: string;
|
||||||
|
|
||||||
|
enableSeparator: boolean;
|
||||||
|
separator?: string;
|
||||||
|
separatorInterval?: number;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { ArrowUndoRegular } from "@fluentui/react-icons";
|
|||||||
import { ReactElement } from "react";
|
import { ReactElement } from "react";
|
||||||
import infoLabel from "../../utils/infoLabel";
|
import infoLabel from "../../utils/infoLabel";
|
||||||
import { useStyles } from "./SettingsSection.styles";
|
import { useStyles } from "./SettingsSection.styles";
|
||||||
|
import { MAX_PASSWORD_LENGTH, MIN_PASSWORD_LENGTH } from "@/utils/constants";
|
||||||
|
|
||||||
export default function SettingsSection(): ReactElement
|
export default function SettingsSection(): ReactElement
|
||||||
{
|
{
|
||||||
@@ -28,7 +29,7 @@ export default function SettingsSection(): ReactElement
|
|||||||
{
|
{
|
||||||
if (e.value.length >= 1)
|
if (e.value.length >= 1)
|
||||||
{
|
{
|
||||||
const value = parseInt(e.value);
|
const value = parseInt(e.value, 10);
|
||||||
|
|
||||||
if (!isNaN(value) && value >= 0)
|
if (!isNaN(value) && value >= 0)
|
||||||
updateStorage({ [key]: value });
|
updateStorage({ [key]: value });
|
||||||
@@ -37,12 +38,38 @@ export default function SettingsSection(): ReactElement
|
|||||||
updateStorage({ [key]: defaultValue });
|
updateStorage({ [key]: defaultValue });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const validateMinLimit = () =>
|
||||||
|
{
|
||||||
|
if (extOptions.MinLength < MIN_PASSWORD_LENGTH)
|
||||||
|
updateStorage({ MinLength: MIN_PASSWORD_LENGTH });
|
||||||
|
else if (extOptions.MinLength > MAX_PASSWORD_LENGTH - 1)
|
||||||
|
updateStorage({ MinLength: MAX_PASSWORD_LENGTH - 1, MaxLength: MAX_PASSWORD_LENGTH });
|
||||||
|
else if (extOptions.MinLength >= extOptions.MaxLength)
|
||||||
|
updateStorage({ MaxLength: extOptions.MinLength + 1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateMaxLimit = () =>
|
||||||
|
{
|
||||||
|
if (extOptions.MaxLength > MAX_PASSWORD_LENGTH)
|
||||||
|
updateStorage({ MaxLength: MAX_PASSWORD_LENGTH });
|
||||||
|
else if (extOptions.MaxLength < MIN_PASSWORD_LENGTH + 1)
|
||||||
|
updateStorage({ MinLength: MIN_PASSWORD_LENGTH, MaxLength: MIN_PASSWORD_LENGTH + 1 });
|
||||||
|
else if (extOptions.MaxLength <= extOptions.MinLength)
|
||||||
|
updateStorage({ MinLength: extOptions.MaxLength - 1 });
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateLength = () =>
|
||||||
|
{
|
||||||
|
updateStorage({ Length: Math.max(Math.min(generatorOptions.Length, extOptions.MaxLength), extOptions.MinLength) });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={ cls.root }>
|
<section className={ cls.root }>
|
||||||
|
|
||||||
<fui.Field label={ i18n.t("settings.length.title") } hint={ i18n.t("settings.length.hint") }>
|
<fui.Field label={ i18n.t("settings.length.title") } hint={ i18n.t("settings.length.hint") }>
|
||||||
<fui.Input
|
<fui.Input
|
||||||
value={ generatorOptions.Length.toString() }
|
value={ generatorOptions.Length.toString() }
|
||||||
|
onBlur={ validateLength }
|
||||||
onChange={ updateNumberField("Length", 0) } />
|
onChange={ updateNumberField("Length", 0) } />
|
||||||
</fui.Field>
|
</fui.Field>
|
||||||
|
|
||||||
@@ -51,6 +78,7 @@ export default function SettingsSection(): ReactElement
|
|||||||
<fui.Input
|
<fui.Input
|
||||||
input={ { className: cls.rangeInput } }
|
input={ { className: cls.rangeInput } }
|
||||||
value={ extOptions.MinLength.toString() }
|
value={ extOptions.MinLength.toString() }
|
||||||
|
onBlur={ validateMinLimit }
|
||||||
onChange={ updateNumberField("MinLength", defaultOptions.extension.MinLength) } />
|
onChange={ updateNumberField("MinLength", defaultOptions.extension.MinLength) } />
|
||||||
|
|
||||||
<fui.Divider />
|
<fui.Divider />
|
||||||
@@ -58,6 +86,7 @@ export default function SettingsSection(): ReactElement
|
|||||||
<fui.Input
|
<fui.Input
|
||||||
input={ { className: cls.rangeInput } }
|
input={ { className: cls.rangeInput } }
|
||||||
value={ extOptions.MaxLength.toString() }
|
value={ extOptions.MaxLength.toString() }
|
||||||
|
onBlur={ validateMaxLimit }
|
||||||
onChange={ updateNumberField("MaxLength", defaultOptions.extension.MaxLength) } />
|
onChange={ updateNumberField("MaxLength", defaultOptions.extension.MaxLength) } />
|
||||||
|
|
||||||
<fui.Tooltip relationship="label" content={ i18n.t("common.actions.reset") }>
|
<fui.Tooltip relationship="label" content={ i18n.t("common.actions.reset") }>
|
||||||
@@ -84,7 +113,7 @@ export default function SettingsSection(): ReactElement
|
|||||||
checked={ generatorOptions.Numeric }
|
checked={ generatorOptions.Numeric }
|
||||||
onChange={ setOption("Numeric") } />
|
onChange={ setOption("Numeric") } />
|
||||||
<fui.Checkbox
|
<fui.Checkbox
|
||||||
label={ infoLabel(i18n.t("common.characters.special"), CharacterHints.special) }
|
label={ infoLabel(i18n.t("common.characters.special"), CharacterHints.special, true) }
|
||||||
checked={ generatorOptions.Special }
|
checked={ generatorOptions.Special }
|
||||||
onChange={ setOption("Special") } />
|
onChange={ setOption("Special") } />
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "Password generator"
|
title: "Password generator"
|
||||||
length: "Password length"
|
length: "Password length"
|
||||||
min_of_type: "Minimum number of characters"
|
min_of_type: "Minimum number of characters"
|
||||||
|
separator1: "Add separator"
|
||||||
|
separator2: "every"
|
||||||
|
separator3: "character(s)"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "Passphrase generator"
|
title: "Passphrase generator"
|
||||||
length: "Number of words"
|
length: "Number of words"
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "Generator haseł"
|
title: "Generator haseł"
|
||||||
length: "Długość hasła"
|
length: "Długość hasła"
|
||||||
min_of_type: "Minimum"
|
min_of_type: "Minimum"
|
||||||
|
separator1: "Dodaj separator"
|
||||||
|
separator2: "co"
|
||||||
|
separator3: "znak(ów)"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "Frazy hasłowe"
|
title: "Frazy hasłowe"
|
||||||
length: "Liczba słów"
|
length: "Liczba słów"
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "Gerador de senhas"
|
title: "Gerador de senhas"
|
||||||
length: "Comprimento da senha"
|
length: "Comprimento da senha"
|
||||||
min_of_type: "Número mínimo de caracteres"
|
min_of_type: "Número mínimo de caracteres"
|
||||||
|
separator1: "Adicionar separador"
|
||||||
|
separator2: "a cada"
|
||||||
|
separator3: "caractere(s)"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "Gerador de frases-senha"
|
title: "Gerador de frases-senha"
|
||||||
length: "Número de palavras"
|
length: "Número de palavras"
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "Генератор паролей"
|
title: "Генератор паролей"
|
||||||
length: "Длина пароля"
|
length: "Длина пароля"
|
||||||
min_of_type: "Не менее"
|
min_of_type: "Не менее"
|
||||||
|
separator1: "Добавить разделитель"
|
||||||
|
separator2: "каждые"
|
||||||
|
separator3: "символов"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "Парольных фраз"
|
title: "Парольных фраз"
|
||||||
length: "Количество слов"
|
length: "Количество слов"
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "Генератор паролів"
|
title: "Генератор паролів"
|
||||||
length: "Довжина пароля"
|
length: "Довжина пароля"
|
||||||
min_of_type: "Не менше"
|
min_of_type: "Не менше"
|
||||||
|
separator1: "Додати розділювач"
|
||||||
|
separator2: "кожні"
|
||||||
|
separator3: "символів"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "Парольних фраз"
|
title: "Парольних фраз"
|
||||||
length: "Кількість слів"
|
length: "Кількість слів"
|
||||||
|
|||||||
@@ -69,6 +69,9 @@ advanced:
|
|||||||
title: "密码生成器"
|
title: "密码生成器"
|
||||||
length: "密码长度"
|
length: "密码长度"
|
||||||
min_of_type: "最少字符数"
|
min_of_type: "最少字符数"
|
||||||
|
separator1: "添加分隔符"
|
||||||
|
separator2: "每"
|
||||||
|
separator3: "个字符"
|
||||||
passphrase:
|
passphrase:
|
||||||
title: "密码短语生成器"
|
title: "密码短语生成器"
|
||||||
length: "单词数量"
|
length: "单词数量"
|
||||||
|
|||||||
Generated
+808
-871
File diff suppressed because it is too large
Load Diff
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "password-generator",
|
"name": "password-generator",
|
||||||
"version": "5.0.5",
|
"version": "5.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -15,21 +15,21 @@
|
|||||||
"postinstall": "wxt prepare"
|
"postinstall": "wxt prepare"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fluentui/react-components": "^9.64.0",
|
"@fluentui/react-components": "^9.66.5",
|
||||||
"@fluentui/react-icons": "^2.0.302",
|
"@fluentui/react-icons": "^2.0.305",
|
||||||
"@wxt-dev/i18n": "^0.2.4",
|
"@wxt-dev/i18n": "^0.2.4",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
"react-responsive": "^10.0.1"
|
"react-responsive": "^10.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.28.0",
|
"@eslint/js": "^9.30.0",
|
||||||
"@types/react": "^18.3.12",
|
"@types/react": "^18.3.12",
|
||||||
"@types/react-dom": "^18.3.1",
|
"@types/react-dom": "^18.3.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.33.1",
|
"@typescript-eslint/eslint-plugin": "^8.35.1",
|
||||||
"@typescript-eslint/parser": "^8.33.0",
|
"@typescript-eslint/parser": "^8.35.1",
|
||||||
"@wxt-dev/module-react": "^1.1.3",
|
"@wxt-dev/module-react": "^1.1.3",
|
||||||
"eslint": "^9.28.0",
|
"eslint": "^9.30.0",
|
||||||
"eslint-plugin-react": "^7.37.5",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"globals": "^16.2.0",
|
"globals": "^16.2.0",
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export const MIN_PASSWORD_LENGTH = 4;
|
||||||
|
export const MAX_PASSWORD_LENGTH = 512;
|
||||||
|
export const DEFAULT_PASSWORD_LENGTH = 8;
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { MIN_PASSWORD_LENGTH } from "../constants";
|
||||||
import { pickRandomFromArray, shuffleString } from "./randomUtils";
|
import { pickRandomFromArray, shuffleString } from "./randomUtils";
|
||||||
|
|
||||||
const Characters =
|
const Characters =
|
||||||
@@ -37,6 +38,10 @@ export function generatePassword(options: PasswordProps): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
password = shuffleString(password);
|
password = shuffleString(password);
|
||||||
|
|
||||||
|
if (options.separator && options.separatorInterval)
|
||||||
|
password = addSeparator(password, options.separator, options.separatorInterval);
|
||||||
|
|
||||||
return password;
|
return password;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +52,7 @@ export function generatePassword(options: PasswordProps): string
|
|||||||
*/
|
*/
|
||||||
export function validateOptions(options: PasswordProps): void
|
export function validateOptions(options: PasswordProps): void
|
||||||
{
|
{
|
||||||
if (options.length < 4)
|
if (options.length < MIN_PASSWORD_LENGTH)
|
||||||
throw new Error(i18n.t("errors.too_short"));
|
throw new Error(i18n.t("errors.too_short"));
|
||||||
|
|
||||||
const availableCharacters: string = getAvailableCharacters(options);
|
const availableCharacters: string = getAvailableCharacters(options);
|
||||||
@@ -122,6 +127,18 @@ function getRequiredCharacters(options: PasswordProps): string
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addSeparator(password: string, separator: string, separatorInterval: number): string
|
||||||
|
{
|
||||||
|
if (!separator || separatorInterval < 1)
|
||||||
|
return password;
|
||||||
|
|
||||||
|
const parts: string[] = [];
|
||||||
|
for (let i = 0; i < password.length; i += separatorInterval)
|
||||||
|
parts.push(password.slice(i, i + separatorInterval));
|
||||||
|
|
||||||
|
return parts.join(separator);
|
||||||
|
}
|
||||||
|
|
||||||
export type PasswordProps =
|
export type PasswordProps =
|
||||||
{
|
{
|
||||||
length: number;
|
length: number;
|
||||||
@@ -139,4 +156,7 @@ export type PasswordProps =
|
|||||||
|
|
||||||
excludeRepeating: boolean;
|
excludeRepeating: boolean;
|
||||||
excludeCustom: string;
|
excludeCustom: string;
|
||||||
|
|
||||||
|
separator?: string;
|
||||||
|
separatorInterval?: number;
|
||||||
};
|
};
|
||||||
|
|||||||
+12
-7
@@ -1,15 +1,20 @@
|
|||||||
import { InfoLabel, Label, LabelProps, Slot } from "@fluentui/react-components";
|
import { InfoLabel, Label, LabelProps, makeStyles, Slot } from "@fluentui/react-components";
|
||||||
|
|
||||||
// FIXME: Remove ts-ignore comments once slots override fix is released
|
export default function infoLabel(label: string, hint: string, noWrap?: boolean): Slot<typeof Label>
|
||||||
// Tracker: https://github.com/microsoft/fluentui/issues/27090
|
|
||||||
|
|
||||||
export default function infoLabel(label: string, hint: string): Slot<typeof Label>
|
|
||||||
{
|
{
|
||||||
// @ts-expect-error See FIXME
|
const cls = useStyles();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
children: (_: unknown, props: LabelProps) =>
|
children: (_: unknown, props: LabelProps) =>
|
||||||
<InfoLabel { ...props } info={ hint }>
|
<InfoLabel { ...props } info={ { className: noWrap ? cls.noWrap : undefined, children: hint } }>
|
||||||
{ label }
|
{ label }
|
||||||
</InfoLabel>
|
</InfoLabel>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
noWrap:
|
||||||
|
{
|
||||||
|
whiteSpace: "pre"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import { MIN_PASSWORD_LENGTH } from "../constants";
|
||||||
|
|
||||||
export default class ExtensionOptions
|
export default class ExtensionOptions
|
||||||
{
|
{
|
||||||
public MinLength: number = 4;
|
public MinLength: number = MIN_PASSWORD_LENGTH;
|
||||||
public MaxLength: number = 32;
|
public MaxLength: number = 32;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import { DEFAULT_PASSWORD_LENGTH } from "../constants";
|
||||||
|
|
||||||
export default class GeneratorOptions
|
export default class GeneratorOptions
|
||||||
{
|
{
|
||||||
public Length: number = 8;
|
public Length: number = DEFAULT_PASSWORD_LENGTH;
|
||||||
|
|
||||||
public Special: boolean = true;
|
public Special: boolean = true;
|
||||||
public Numeric: boolean = true;
|
public Numeric: boolean = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user