mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-12 02:15:01 +00:00
Add knip (#376)
Export-Source-Commit: 754d2f1b0238a99b266936a27562314c9c74f85b
This commit is contained in:
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2024 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import eslint from './web/packages/build/eslint.config.mjs';
|
||||
export default eslint;
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/knip@6/schema.json",
|
||||
"workspaces": {
|
||||
".": {
|
||||
"entry": [
|
||||
// Required from an inline actions/github-script block in docs-amplify.yaml.
|
||||
".github/scripts/post-preview-links.js",
|
||||
// Run by `node --test`.
|
||||
".github/scripts/test-*.js",
|
||||
"web/.storybook/main.mts",
|
||||
"web/.storybook/preview.tsx",
|
||||
"web/.storybook/vite.config.mts",
|
||||
"web/.storybook/mocks/**/*.{ts,tsx,js}"
|
||||
],
|
||||
"project": [
|
||||
".github/scripts/**/*.js",
|
||||
"web/.storybook/**/*.{ts,tsx,mts,mjs,js}",
|
||||
"web/@types/*.d.ts"
|
||||
],
|
||||
"ignoreDependencies": [
|
||||
// Only the generated gRPC clients under gen/proto/ts reference this, and those are not
|
||||
// part of any project.
|
||||
"@grpc/grpc-js",
|
||||
// Referenced from a JSDoc type annotation in jest.config.js.
|
||||
"@jest/types",
|
||||
// Named as a string in web/.storybook/main.mts `addons`.
|
||||
"@storybook/addon-vitest",
|
||||
// Loaded by name through `jsPlugins` in .oxlintrc.jsonc. The plugins pull in eslint
|
||||
// itself at runtime via @typescript-eslint/utils.
|
||||
"eslint",
|
||||
"eslint-plugin-jest-dom",
|
||||
"eslint-plugin-storybook",
|
||||
"eslint-plugin-testing-library",
|
||||
"eslint-plugin-unused-imports"
|
||||
]
|
||||
},
|
||||
"web/packages/build": {
|
||||
"entry": [
|
||||
"vite/**/*.{ts,mts,mjs}",
|
||||
"jest/**/*.{ts,js}",
|
||||
"storybook.ts",
|
||||
"**/*.test.{ts,tsx,js,jsx}"
|
||||
],
|
||||
// These are loaded by name through the SWC plugin config in vite/react.mjs.
|
||||
"ignoreDependencies": [
|
||||
"@gravitational/swc-plugin-jsx-marker",
|
||||
"@gravitational/swc-plugin-styled-components",
|
||||
"@swc/core"
|
||||
]
|
||||
},
|
||||
"web/packages/design": {
|
||||
"entry": [
|
||||
"src/**/*.story.{ts,tsx,js,jsx}",
|
||||
"src/**/*.test.{ts,tsx,js,jsx}"
|
||||
],
|
||||
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}", "@types/*.d.ts"],
|
||||
"paths": {
|
||||
"design/*": ["./src/*"],
|
||||
"design": ["./src/"]
|
||||
},
|
||||
// Compile-time only assertions, checked by tsc rather than imported anywhere.
|
||||
"ignore": ["src/**/*.type-test.tsx"]
|
||||
},
|
||||
"web/packages/shared": {
|
||||
"entry": ["**/*.story.{ts,tsx,js,jsx}", "**/*.test.{ts,tsx,js,jsx}"],
|
||||
"project": ["**/*.{ts,tsx,js,jsx,mts,mjs}", "!libs/ironrdp/pkg/**"],
|
||||
"paths": {
|
||||
"design/*": ["../design/src/*"],
|
||||
"design": ["../design/src/"],
|
||||
"shared/*": ["./*"],
|
||||
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
|
||||
},
|
||||
"ignoreDependencies": [
|
||||
// Reached through the `design/*` path alias rather than the package name.
|
||||
"@gravitational/design",
|
||||
// Browser polyfill, imported as the bare Node builtin specifier.
|
||||
"events"
|
||||
]
|
||||
},
|
||||
"web/packages/teleport": {
|
||||
"entry": [
|
||||
"babel.config.js",
|
||||
"index.html",
|
||||
"src/**/*.story.{ts,tsx,js,jsx}",
|
||||
"src/**/*.test.{ts,tsx,js,jsx}",
|
||||
"src/services/audit/gen-event-reference/index.ts"
|
||||
],
|
||||
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}"],
|
||||
"paths": {
|
||||
"build/*": ["../build/*"],
|
||||
"shared/*": ["../shared/*"],
|
||||
"design/*": ["../design/src/*"],
|
||||
"design": ["../design/src/"],
|
||||
"teleport/*": ["./src/*"],
|
||||
"teleport": ["./src/"],
|
||||
"teleterm/*": ["../teleterm/src/*"],
|
||||
"e-teleport/*": ["../../../e/web/teleport/src/*"],
|
||||
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
|
||||
},
|
||||
"ignoreDependencies": [
|
||||
// Reached through path aliases rather than package names.
|
||||
"@gravitational/design",
|
||||
"@gravitational/shared",
|
||||
"@gravitational/teleport",
|
||||
// Declared here but named as strings by web/packages/build/.babelrc.js.
|
||||
"babel-plugin-transform-import-meta",
|
||||
"babel-plugin-transform-vite-meta-env",
|
||||
"babel-plugin-transform-vite-meta-hot",
|
||||
// Browser polyfill, imported as the bare Node builtin specifier.
|
||||
"events"
|
||||
]
|
||||
},
|
||||
"web/packages/teleterm": {
|
||||
"entry": [
|
||||
"electron-builder-config.js",
|
||||
"index.html",
|
||||
"src/main.ts",
|
||||
"src/preload.ts",
|
||||
"src/sharedProcess/sharedProcess.ts",
|
||||
"src/agentCleanupDaemon/agentCleanupDaemon.js",
|
||||
"src/**/*.story.{ts,tsx,js,jsx}",
|
||||
"src/**/*.test.{ts,tsx,js,jsx}",
|
||||
"src/**/*Process.mjs"
|
||||
],
|
||||
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}", "csp.ts"],
|
||||
"paths": {
|
||||
"build/*": ["../build/*"],
|
||||
"shared/*": ["../shared/*"],
|
||||
"design/*": ["../design/src/*"],
|
||||
"design": ["../design/src/"],
|
||||
"teleport/*": ["../teleport/src/*"],
|
||||
"teleterm/*": ["./src/*"],
|
||||
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
|
||||
},
|
||||
"ignoreDependencies": [
|
||||
// Reached through path aliases rather than package names.
|
||||
"@gravitational/design",
|
||||
"@gravitational/shared",
|
||||
// Browser polyfill, imported as the bare Node builtin specifier.
|
||||
"events"
|
||||
]
|
||||
},
|
||||
"e/web/teleport": {
|
||||
"entry": [
|
||||
"babel.config.js",
|
||||
"index.html",
|
||||
"src/**/*.story.{ts,tsx,js,jsx}",
|
||||
"src/**/*.test.{ts,tsx,js,jsx}",
|
||||
"src/SessionRecordings/summary/generate-mitre-lookup.ts"
|
||||
],
|
||||
"project": ["src/**/*.{ts,tsx,js,jsx,mts,mjs}"],
|
||||
// Generated protobuf bindings.
|
||||
"ignore": ["src/**/*_pb.ts", "src/**/*_pb.*.ts"],
|
||||
"paths": {
|
||||
"build/*": ["../../../web/packages/build/*"],
|
||||
"shared/*": ["../../../web/packages/shared/*"],
|
||||
"design/*": ["../../../web/packages/design/src/*"],
|
||||
"design": ["../../../web/packages/design/src/"],
|
||||
"teleport/*": ["../../../web/packages/teleport/src/*"],
|
||||
"teleport": ["../../../web/packages/teleport/src/"],
|
||||
"teleterm/*": ["../../../web/packages/teleterm/src/*"],
|
||||
"e-teleport/*": ["./src/*"],
|
||||
"gen-proto-ts/*": ["../../../gen/proto/ts/*"]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"entry": ["tests/**/*.{ts,tsx}", "scripts/**/*.{ts,tsx,js,mjs}"],
|
||||
"project": [
|
||||
"{tests,scripts,helpers,aws,config,node,runner}/**/*.{ts,tsx,js,mjs}"
|
||||
]
|
||||
},
|
||||
"e/e2e": {
|
||||
"entry": ["tests/**/*.{ts,tsx}"],
|
||||
"project": ["{tests,helpers,config}/**/*.{ts,tsx,js,mjs}"]
|
||||
}
|
||||
},
|
||||
"ignoreBinaries": [
|
||||
// Run by .github/workflows/doc-tests.yaml against the separately checked out docs site.
|
||||
"spellcheck",
|
||||
"markdown-lint",
|
||||
"build",
|
||||
"make"
|
||||
],
|
||||
"ignoreUnresolved": [
|
||||
// Provided transitively by jest.
|
||||
"babel-jest",
|
||||
// Declared by web/packages/teleport but named as strings by web/packages/build/.babelrc.js.
|
||||
"babel-plugin-transform-import-meta",
|
||||
"babel-plugin-transform-vite-meta-env",
|
||||
"babel-plugin-transform-vite-meta-hot",
|
||||
// Deliberately missing, so the code under test has to handle a failed spawn.
|
||||
"./testProcess-nonExistent.js",
|
||||
"./testProcess-nonExisting.mjs"
|
||||
],
|
||||
"rules": {
|
||||
// The design system deliberately keeps deprecated aliases (OutlineDanger, the FieldSelect
|
||||
// default export) alongside their replacements.
|
||||
"duplicates": "off",
|
||||
"enumMembers": "off"
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -13,13 +13,12 @@
|
||||
"package-term": "pnpm --filter=@gravitational/teleterm package",
|
||||
"storybook": "./web/scripts/run-storybook.sh",
|
||||
"storybook-smoke-test": "storybook dev -p 9002 -c web/.storybook --ci --smoke-test",
|
||||
"test-storybook": "NODE_TLS_REJECT_UNAUTHORIZED=0 test-storybook -c web/.storybook --url https://localhost:9002 --skipTags=skip-test --browsers=chromium",
|
||||
"test": "jest",
|
||||
"test-coverage": "jest --coverage && web/scripts/print-coverage-link.sh",
|
||||
"test-update-snapshot": "pnpm run test --updateSnapshot",
|
||||
"tdd": "jest --watch",
|
||||
"oxlint": "oxlint --quiet --report-unused-disable-directives-severity error web/ e/ e2e/",
|
||||
"lint": "pnpm oxlint && pnpm format-check",
|
||||
"lint": "pnpm oxlint && pnpm format-check && pnpm knip",
|
||||
"lint-fix": "pnpm oxlint --fix && pnpm format",
|
||||
"type-check": "tsc --build",
|
||||
"optimize-resource-icons": "svgo --multipass --quiet -rf web/packages/design/src/ResourceIcon/assets",
|
||||
@@ -27,6 +26,7 @@
|
||||
"format": "oxfmt 'e/**/*.{ts,tsx,js,jsx,mts}' 'web/**/*.{ts,tsx,js,jsx,mts}' 'e2e/**/*.{ts,tsx,js,jsx,mts}'",
|
||||
"process-icons": "pnpm optimize-resource-icons && node web/packages/design/src/Icon/script/script.js && pnpm oxfmt 'web/packages/design/src/Icon/**/*.tsx'",
|
||||
"list-installed-versions": "./web/scripts/list-installed-versions.sh",
|
||||
"knip": "KNIP=1 knip",
|
||||
"postinstall": "pnpm generate-theme",
|
||||
"generate-theme": "design-system typegen",
|
||||
"nop": "exit 0"
|
||||
@@ -46,7 +46,6 @@
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"@types/wicg-file-system-access": "^2023.10.7",
|
||||
"cspell": "^10.0.1",
|
||||
"eslint": "^10.8.1",
|
||||
"eslint-plugin-jest-dom": "^5.10.1",
|
||||
"eslint-plugin-storybook": "^10.5.7",
|
||||
@@ -54,6 +53,7 @@
|
||||
"eslint-plugin-unused-imports": "^4.4.1",
|
||||
"jest": "^30.4.2",
|
||||
"jsdom-testing-mocks": "^1.16.0",
|
||||
"knip": "^6.32.2",
|
||||
"msw": "2.15.0",
|
||||
"msw-storybook-addon": "^3.0.0",
|
||||
"oxfmt": "^0.62.0",
|
||||
|
||||
Generated
+338
-748
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,6 @@
|
||||
"name": "@gravitational/build",
|
||||
"version": "1.0.1",
|
||||
"description": "build scripts",
|
||||
"main": "index.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -19,7 +18,6 @@
|
||||
"@swc/core": "^1.15.47",
|
||||
"@vitejs/plugin-react-swc": "^4.3.3",
|
||||
"babel-plugin-styled-components": "^2.3.0",
|
||||
"globals": "^17.9.0",
|
||||
"jest-environment-jsdom": "^30.4.1",
|
||||
"jest-fail-on-console": "^3.3.4",
|
||||
"rollup-plugin-visualizer": "^7.0.1",
|
||||
|
||||
@@ -40,9 +40,13 @@ export function createViteConfig(
|
||||
outputDirectory: string
|
||||
) {
|
||||
return defineConfig(({ mode }) => {
|
||||
// Knip loads this config to enumerate entry points. There is no dev server in that context, so
|
||||
// skip the parts of the setup it cannot satisfy: the proxy target notice and the certificates.
|
||||
const isKnip = !!process.env.KNIP;
|
||||
|
||||
let target = resolveTargetURL(process.env.PROXY_TARGET);
|
||||
|
||||
if (mode === 'development') {
|
||||
if (mode === 'development' && !isKnip) {
|
||||
if (process.env.PROXY_TARGET) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
@@ -213,7 +217,7 @@ export function createViteConfig(
|
||||
key: readFileSync(process.env.VITE_HTTPS_KEY),
|
||||
cert: readFileSync(process.env.VITE_HTTPS_CERT),
|
||||
};
|
||||
} else {
|
||||
} else if (!isKnip) {
|
||||
const certsDirectory = resolve(rootDirectory, 'web/certs');
|
||||
|
||||
if (!existsSync(certsDirectory)) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"name": "@gravitational/design",
|
||||
"version": "1.0.0",
|
||||
"description": "Gravitational design system",
|
||||
"main": "index.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,7 +14,6 @@
|
||||
"styled-system": "^5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gravitational/build": "workspace:*",
|
||||
"@types/styled-system": "^5.1.25",
|
||||
"jest-styled-components": "^7.4.0"
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ export const Button = <E extends React.ElementType = 'button'>({
|
||||
|
||||
Button.displayName = 'Button';
|
||||
|
||||
export type ThemedButtonProps<E extends React.ElementType> = ButtonProps<E> & {
|
||||
type ThemedButtonProps<E extends React.ElementType> = ButtonProps<E> & {
|
||||
theme: Theme;
|
||||
fill: ButtonFill;
|
||||
intent: ButtonIntent;
|
||||
|
||||
@@ -22,7 +22,6 @@ import CardError, {
|
||||
LoginFailed,
|
||||
LogoutFailed,
|
||||
NotFound,
|
||||
Offline,
|
||||
BadRequest,
|
||||
} from './CardError';
|
||||
|
||||
@@ -32,7 +31,6 @@ export {
|
||||
LoginFailed,
|
||||
AccessDenied,
|
||||
NotFound,
|
||||
Offline,
|
||||
LogoutFailed,
|
||||
BadRequest,
|
||||
};
|
||||
|
||||
@@ -122,7 +122,7 @@ type TableColumnBase<T> = {
|
||||
|
||||
export type PagerPosition = 'top' | 'bottom' | 'both';
|
||||
|
||||
export type PaginationConfig<T> = {
|
||||
type PaginationConfig<T> = {
|
||||
pageSize?: number;
|
||||
/**
|
||||
* "undefined" will show both pagers if data on current page is some
|
||||
|
||||
@@ -240,28 +240,7 @@ export const Primary = (props: LabelPropsWithoutKind) => (
|
||||
/** @deprecated Use `<Tag>` from `design/Tag`. */
|
||||
export const Secondary = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="secondary" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Status kind="warning" variant="filled">` from `design/Status`. */
|
||||
export const Warning = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="warning" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Status kind="danger" variant="filled">` from `design/Status`. */
|
||||
export const Danger = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="danger" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Tag variant="outline">` from `design/Tag`. */
|
||||
); /** @deprecated Use `<Tag variant="outline">` from `design/Tag`. */
|
||||
export const SecondaryOutlined = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="outline-secondary" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Status kind="success">` from `design/Status`. */
|
||||
export const SuccessOutlined = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="outline-success" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Status kind="warning">` from `design/Status`. */
|
||||
export const WarningOutlined = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="outline-warning" {...props} />
|
||||
);
|
||||
/** @deprecated Use `<Status kind="danger">` from `design/Status`. */
|
||||
export const DangerOutlined = (props: LabelPropsWithoutKind) => (
|
||||
<Label kind="outline-danger" {...props} />
|
||||
);
|
||||
|
||||
@@ -16,18 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Label, {
|
||||
Danger,
|
||||
DangerOutlined,
|
||||
Primary,
|
||||
Secondary,
|
||||
SecondaryOutlined,
|
||||
SuccessOutlined,
|
||||
Warning,
|
||||
WarningOutlined,
|
||||
} from './Label';
|
||||
import Label from './Label';
|
||||
|
||||
export default Label;
|
||||
export { Primary, Secondary, Warning, Danger };
|
||||
export { SecondaryOutlined, SuccessOutlined, WarningOutlined, DangerOutlined };
|
||||
export type { LabelKind } from './Label';
|
||||
|
||||
@@ -30,12 +30,7 @@ import {
|
||||
} from 'design/system';
|
||||
import { Theme } from 'design/theme/themes/types';
|
||||
|
||||
export type LabelKind =
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'warning'
|
||||
| 'danger'
|
||||
| 'success';
|
||||
type LabelKind = 'primary' | 'secondary' | 'warning' | 'danger' | 'success';
|
||||
|
||||
interface KindsProps {
|
||||
kind?: LabelKind;
|
||||
@@ -106,18 +101,3 @@ const LabelState = styled.span<LabelStateProps>`
|
||||
`;
|
||||
|
||||
export default LabelState;
|
||||
|
||||
type LabelStatePropsWithoutKind = Omit<LabelStateProps, 'kind'>;
|
||||
|
||||
export const StateDanger = (props: LabelStatePropsWithoutKind) => (
|
||||
<LabelState kind="danger" {...props} />
|
||||
);
|
||||
export const StateInfo = (props: LabelStatePropsWithoutKind) => (
|
||||
<LabelState kind="secondary" {...props} />
|
||||
);
|
||||
export const StateWarning = (props: LabelStatePropsWithoutKind) => (
|
||||
<LabelState kind="warning" {...props} />
|
||||
);
|
||||
export const StateSuccess = (props: LabelStatePropsWithoutKind) => (
|
||||
<LabelState kind="success" {...props} />
|
||||
);
|
||||
|
||||
@@ -16,13 +16,6 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import LabelState, {
|
||||
StateDanger,
|
||||
StateInfo,
|
||||
StateSuccess,
|
||||
StateWarning,
|
||||
} from './LabelState';
|
||||
import LabelState from './LabelState';
|
||||
|
||||
export default LabelState;
|
||||
|
||||
export { StateDanger, StateInfo, StateWarning, StateSuccess };
|
||||
|
||||
@@ -56,8 +56,8 @@ export type Origin = {
|
||||
vertical: VerticalOrigin;
|
||||
};
|
||||
|
||||
export type HorizontalOrigin = 'left' | 'center' | 'right' | number;
|
||||
export type VerticalOrigin = 'top' | 'center' | 'bottom' | number;
|
||||
type HorizontalOrigin = 'left' | 'center' | 'right' | number;
|
||||
type VerticalOrigin = 'top' | 'center' | 'bottom' | number;
|
||||
export type GrowDirections = 'top-left' | 'bottom-right';
|
||||
export type Position = 'top' | 'right' | 'bottom' | 'left';
|
||||
|
||||
@@ -437,7 +437,7 @@ function getArrowPaddingProp(
|
||||
}
|
||||
}
|
||||
|
||||
export class Popover extends Component<Props> {
|
||||
class Popover extends Component<Props> {
|
||||
paperRef = createRef<HTMLDivElement>();
|
||||
handleResize: () => void = () => {};
|
||||
|
||||
|
||||
@@ -21,10 +21,7 @@ import Popover from './Popover';
|
||||
export {
|
||||
StyledPopover,
|
||||
type Origin,
|
||||
type HorizontalOrigin,
|
||||
type VerticalOrigin,
|
||||
type GrowDirections,
|
||||
type Position as Orientation,
|
||||
type Props as PopoverProps,
|
||||
} from './Popover';
|
||||
|
||||
|
||||
@@ -152,7 +152,7 @@ function indicatorSize(props: { rbSize?: RadioButtonSize }) {
|
||||
return { width: s, height: s };
|
||||
}
|
||||
|
||||
export const RadioButtonInternal = styled.input.attrs({ type: 'radio' })<{
|
||||
const RadioButtonInternal = styled.input.attrs({ type: 'radio' })<{
|
||||
rbSize?: RadioButtonSize;
|
||||
}>`
|
||||
appearance: none;
|
||||
|
||||
@@ -17,6 +17,3 @@
|
||||
*/
|
||||
|
||||
export { TeleportGearIcon } from './TeleportGearIcon';
|
||||
|
||||
export { SVGIcon } from './SVGIcon';
|
||||
export type { SVGIconProps } from './common';
|
||||
|
||||
@@ -18,5 +18,4 @@
|
||||
|
||||
export { Status } from './Status';
|
||||
export { StatusDot } from './StatusDot';
|
||||
export type { StatusDotProps } from './StatusDot';
|
||||
export type { StatusKind, StatusVariant, StatusProps } from './Status';
|
||||
export type { StatusKind, StatusVariant } from './Status';
|
||||
|
||||
@@ -24,7 +24,7 @@ import { space, type SpaceProps } from 'design/system';
|
||||
|
||||
import { pillBase } from '../pillStyles';
|
||||
|
||||
export type TagVariant = 'subtle' | 'outline';
|
||||
type TagVariant = 'subtle' | 'outline';
|
||||
|
||||
export interface TagProps
|
||||
extends SpaceProps, Omit<HTMLAttributes<HTMLSpanElement>, 'color'> {
|
||||
|
||||
@@ -17,4 +17,3 @@
|
||||
*/
|
||||
|
||||
export { Tag } from './Tag';
|
||||
export type { TagVariant, TagProps } from './Tag';
|
||||
|
||||
@@ -67,7 +67,7 @@ export interface TextAreaProps
|
||||
onClick?: React.MouseEventHandler<HTMLInputElement>;
|
||||
}
|
||||
|
||||
export const textAreaGeometry: {
|
||||
const textAreaGeometry: {
|
||||
[s in TextAreaSize]: {
|
||||
height: number;
|
||||
typography: keyof Theme['typography'];
|
||||
|
||||
@@ -86,11 +86,6 @@ export function displayDateWithPrefixedTime(date: Date): string {
|
||||
}
|
||||
}
|
||||
|
||||
/** Converts a UNIX timestamp do the Date object. */
|
||||
export function unixTimestampToDate(seconds: number): Date {
|
||||
return new Date(seconds * 1000);
|
||||
}
|
||||
|
||||
function dateToUtc(date: Date): Date {
|
||||
return new Date(date.getTime() + date.getTimezoneOffset() * 60 * 1000);
|
||||
}
|
||||
|
||||
@@ -40,17 +40,15 @@ import { Indicator } from './Indicator';
|
||||
import Input from './Input';
|
||||
import Label from './Label';
|
||||
import { LabelInput } from './LabelInput';
|
||||
import LabelState from './LabelState';
|
||||
import Link from './Link';
|
||||
import { Mark } from './Mark';
|
||||
import Menu, { MenuItem, MenuItemIcon } from './Menu';
|
||||
import Menu, { MenuItem } from './Menu';
|
||||
import { Pill } from './Pill';
|
||||
import Popover from './Popover';
|
||||
import { ResourceIcon } from './ResourceIcon';
|
||||
import { Status } from './Status';
|
||||
import { StepSlider } from './StepSlider';
|
||||
import { SyncStamp } from './SyncStamp';
|
||||
import { Tag } from './Tag';
|
||||
import Text, {
|
||||
H1,
|
||||
H2,
|
||||
@@ -98,12 +96,10 @@ export {
|
||||
Input,
|
||||
Label,
|
||||
LabelInput,
|
||||
LabelState,
|
||||
Link,
|
||||
Mark,
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuItemIcon,
|
||||
P1,
|
||||
P2,
|
||||
P3,
|
||||
@@ -115,13 +111,11 @@ export {
|
||||
Subtitle2,
|
||||
Subtitle3,
|
||||
SyncStamp,
|
||||
Tag,
|
||||
Text,
|
||||
TextArea,
|
||||
Toggle,
|
||||
TopNav,
|
||||
};
|
||||
export type { TextAreaProps } from './TextArea';
|
||||
export * from './keyframes';
|
||||
export { Stack } from './Flex';
|
||||
export { breakpointsPx } from './theme';
|
||||
|
||||
@@ -42,7 +42,6 @@ import {
|
||||
minWidth,
|
||||
overflow,
|
||||
ResponsiveValue,
|
||||
size,
|
||||
space,
|
||||
style,
|
||||
textAlign,
|
||||
@@ -51,7 +50,6 @@ import {
|
||||
type AlignItemsProps,
|
||||
type AlignSelfProps,
|
||||
type BackgroundProps,
|
||||
type BorderColorProps,
|
||||
type BorderProps,
|
||||
type BorderRadiusProps,
|
||||
type BordersProps,
|
||||
@@ -61,7 +59,6 @@ import {
|
||||
type FlexProps,
|
||||
type FlexWrapProps,
|
||||
type FontSizeProps,
|
||||
type FontWeightProps,
|
||||
type HeightProps,
|
||||
type JustifyContentProps,
|
||||
type JustifySelfProps,
|
||||
@@ -71,7 +68,6 @@ import {
|
||||
type MinHeightProps,
|
||||
type MinWidthProps,
|
||||
type OverflowProps,
|
||||
type SizeProps,
|
||||
type SpaceProps,
|
||||
type TextAlignProps,
|
||||
type WidthProps,
|
||||
@@ -144,7 +140,6 @@ export {
|
||||
border,
|
||||
type BorderProps,
|
||||
borderColor,
|
||||
type BorderColorProps,
|
||||
borders,
|
||||
type BordersProps,
|
||||
borderRadius,
|
||||
@@ -162,7 +157,6 @@ export {
|
||||
fontSize,
|
||||
type FontSizeProps,
|
||||
fontWeight,
|
||||
type FontWeightProps,
|
||||
height,
|
||||
type HeightProps,
|
||||
justifyContent,
|
||||
@@ -181,8 +175,6 @@ export {
|
||||
type MinWidthProps,
|
||||
overflow,
|
||||
type OverflowProps,
|
||||
size,
|
||||
type SizeProps,
|
||||
space,
|
||||
type SpaceProps,
|
||||
textAlign,
|
||||
|
||||
@@ -63,7 +63,7 @@ function clamp(value, min = 0, max = 1) {
|
||||
* @param {string} color - Hex color, i.e. #nnn or #nnnnnn
|
||||
* @returns {string} A CSS rgb color string
|
||||
*/
|
||||
export function convertHexToRGB(color) {
|
||||
function convertHexToRGB(color) {
|
||||
color = color.substr(1);
|
||||
|
||||
const re = new RegExp(`.{1,${color.length / 3}}`, 'g');
|
||||
@@ -76,28 +76,6 @@ export function convertHexToRGB(color) {
|
||||
return colors ? `rgb(${colors.map(n => parseInt(n, 16)).join(', ')})` : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a color from CSS rgb format to CSS hex format.
|
||||
*
|
||||
* @param {string} color - RGB color, i.e. rgb(n, n, n)
|
||||
* @returns {string} A CSS rgb color string, i.e. #nnnnnn
|
||||
*/
|
||||
export function rgbToHex(color) {
|
||||
// Pass hex straight through
|
||||
if (color.indexOf('#') === 0) {
|
||||
return color;
|
||||
}
|
||||
function intToHex(c) {
|
||||
const hex = c.toString(16);
|
||||
return hex.length === 1 ? `0${hex}` : hex;
|
||||
}
|
||||
|
||||
let { values } = decomposeColor(color);
|
||||
values = values.map(n => intToHex(n));
|
||||
|
||||
return `#${values.join('')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object with the type and values of a color.
|
||||
*
|
||||
@@ -142,7 +120,7 @@ export function decomposeColor(color) {
|
||||
* @param {array} color.values - [n,n,n] or [n,n,n,n]
|
||||
* @returns {string} A CSS color string
|
||||
*/
|
||||
export function recomposeColor(color) {
|
||||
function recomposeColor(color) {
|
||||
const { type } = color;
|
||||
let { values } = color;
|
||||
|
||||
@@ -183,7 +161,7 @@ export function getContrastRatio(foreground, background) {
|
||||
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
|
||||
* @returns {number} The relative brightness of the color in the range 0 - 1
|
||||
*/
|
||||
export function getLuminance(color) {
|
||||
function getLuminance(color) {
|
||||
const decomposedColor = decomposeColor(color);
|
||||
|
||||
if (decomposedColor.type.indexOf('rgb') !== -1) {
|
||||
@@ -243,7 +221,7 @@ export function darken(color, coefficient) {
|
||||
* @param {number} coefficient - multiplier in the range 0 - 1
|
||||
* @returns {string} A CSS color string. Hex input values are returned as rgb
|
||||
*/
|
||||
export function lighten(color, coefficient) {
|
||||
function lighten(color, coefficient) {
|
||||
color = decomposeColor(color);
|
||||
coefficient = clamp(coefficient);
|
||||
|
||||
|
||||
@@ -45,8 +45,6 @@ export default function renderHook<T extends (...args: any) => any>(
|
||||
return result;
|
||||
}
|
||||
|
||||
export { act };
|
||||
|
||||
// A wrapper to execute hooks during renderer process
|
||||
function TestHook(props: any) {
|
||||
// trigger hooks and assessing its results to props.current
|
||||
|
||||
@@ -26,7 +26,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
getByTestId,
|
||||
prettyDOM,
|
||||
screen,
|
||||
render as testingRender,
|
||||
@@ -234,9 +233,7 @@ export {
|
||||
testThemeSystem,
|
||||
tick,
|
||||
render,
|
||||
prettyDOM,
|
||||
waitFor,
|
||||
getByTestId,
|
||||
MemoryRouter as Router,
|
||||
userEvent,
|
||||
waitForElementToBeRemoved,
|
||||
|
||||
-1
@@ -17,4 +17,3 @@
|
||||
*/
|
||||
|
||||
export { RequestDelete } from './RequestDelete';
|
||||
export type { RequestDeleteProps } from './RequestDelete';
|
||||
|
||||
+1
-1
@@ -391,7 +391,7 @@ export function RequestorTimestamp({
|
||||
);
|
||||
}
|
||||
|
||||
export function Timestamp({
|
||||
function Timestamp({
|
||||
author,
|
||||
display,
|
||||
state,
|
||||
|
||||
@@ -533,9 +533,3 @@ export const requestRoleEmpty: AccessRequest = {
|
||||
roles: ['empty-values'],
|
||||
id: 'ffc11a95-e8af-581c-ba82-47c429c841e8',
|
||||
};
|
||||
|
||||
export const requests = [
|
||||
requestRolePending,
|
||||
requestRoleDenied,
|
||||
requestRoleApproved,
|
||||
];
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import {
|
||||
KeywordHighlight,
|
||||
SelectedLines,
|
||||
TerminalContent,
|
||||
} from 'shared/components/AnimatedTerminal/TerminalContent';
|
||||
import { Window } from 'shared/components/Window';
|
||||
|
||||
import { BufferEntry, createTerminalContent, TerminalLine } from './content';
|
||||
|
||||
interface AnimatedTerminalProps {
|
||||
lines: TerminalLine[];
|
||||
startDelay?: number;
|
||||
highlights?: KeywordHighlight[];
|
||||
selectedLines?: SelectedLines;
|
||||
stopped?: boolean;
|
||||
onCompleted?: () => void;
|
||||
}
|
||||
|
||||
export function AnimatedTerminal(props: AnimatedTerminalProps) {
|
||||
const lastLineIndex = useRef(0);
|
||||
const content = useMemo(
|
||||
() => createTerminalContent(props.lines, lastLineIndex.current),
|
||||
[props.lines]
|
||||
);
|
||||
|
||||
const [counter, setCounter] = useState(0);
|
||||
const [completed, setCompletedState] = useState(false);
|
||||
|
||||
const lines = useRef<BufferEntry[]>([]);
|
||||
|
||||
function setCompleted(completed: boolean) {
|
||||
setCompletedState(completed);
|
||||
if (completed) {
|
||||
props.onCompleted && props.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let timeout: number;
|
||||
let request: number;
|
||||
|
||||
async function animate() {
|
||||
const { value, done } = await content.next();
|
||||
|
||||
if (value) {
|
||||
if (value.length) {
|
||||
const nextLineIndex = value[value.length - 1].id + 1;
|
||||
if (nextLineIndex > lastLineIndex.current) {
|
||||
lastLineIndex.current = nextLineIndex;
|
||||
}
|
||||
}
|
||||
|
||||
lines.current = value;
|
||||
setCounter(count => count + 1);
|
||||
}
|
||||
|
||||
if (done) {
|
||||
setCompleted(true);
|
||||
setCounter(count => count + 1);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
request = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
function setup() {
|
||||
request = requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
if (!props.startDelay) {
|
||||
setup();
|
||||
} else {
|
||||
timeout = window.setTimeout(setup, props.startDelay);
|
||||
}
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(request);
|
||||
clearTimeout(timeout);
|
||||
};
|
||||
}, [props.startDelay, props.lines, content]);
|
||||
|
||||
let renderedLines = lines.current;
|
||||
if (props.stopped) {
|
||||
renderedLines = props.lines.map((line, index) => ({
|
||||
id: index,
|
||||
text: line.text,
|
||||
isCommand: line.isCommand,
|
||||
isCurrent: index === props.lines.length - 1,
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<Window title="Terminal">
|
||||
<TerminalContent
|
||||
lines={renderedLines}
|
||||
completed={completed}
|
||||
counter={counter}
|
||||
highlights={props.highlights}
|
||||
selectedLines={props.selectedLines}
|
||||
/>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Fragment, useEffect, useLayoutEffect, useRef } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { BufferEntry } from 'shared/components/AnimatedTerminal/content';
|
||||
|
||||
export interface SelectedLines {
|
||||
start: number;
|
||||
end: number;
|
||||
}
|
||||
|
||||
interface TerminalContentProps {
|
||||
lines: BufferEntry[];
|
||||
completed: boolean;
|
||||
counter: number;
|
||||
selectedLines?: SelectedLines;
|
||||
highlights?: KeywordHighlight[];
|
||||
}
|
||||
|
||||
export interface KeywordHighlight {
|
||||
key: string;
|
||||
match?: RegExp;
|
||||
keywords?: string[];
|
||||
color: TerminalColor;
|
||||
}
|
||||
|
||||
export enum TerminalColor {
|
||||
Argument = '#cfa7ff',
|
||||
Keyword = '#5af78e',
|
||||
Error = '#f07278',
|
||||
Label = 'rgba(255, 255, 255, 0.7)',
|
||||
Punctuation = '#81ceee',
|
||||
}
|
||||
|
||||
const SelectedLinesOverlay = styled.div`
|
||||
width: 100%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 0;
|
||||
transform: translate3d(0, 0, 0);
|
||||
transition-property: height;
|
||||
`;
|
||||
|
||||
const Lines = styled.div`
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
`;
|
||||
|
||||
export function TerminalContent(props: TerminalContentProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
ref.current.scrollTop = ref.current.scrollHeight;
|
||||
}, [props.counter]);
|
||||
|
||||
const renderedLines = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.selectedLines) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numberOfLines = props.selectedLines.end - props.selectedLines.start;
|
||||
|
||||
const id = window.setTimeout(() => {
|
||||
renderedLines.current.style.height = `${20 * (numberOfLines + 1)}px`;
|
||||
}, 1000);
|
||||
|
||||
return () => clearTimeout(id);
|
||||
}, [props.selectedLines]);
|
||||
|
||||
let selectedLines;
|
||||
if (props.selectedLines) {
|
||||
const numberOfLines = props.selectedLines.end - props.selectedLines.start;
|
||||
|
||||
selectedLines = (
|
||||
<SelectedLinesOverlay
|
||||
ref={renderedLines}
|
||||
style={{
|
||||
top: 20 * (props.selectedLines.start + 1),
|
||||
transitionTimingFunction: `steps(${numberOfLines + 2}, jump-none)`,
|
||||
transitionDuration: `${numberOfLines * 0.08}s`,
|
||||
height: 0,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TerminalContentContainer ref={ref}>
|
||||
<TerminalCode>
|
||||
<Lines>{renderLines(props.lines, props.highlights)}</Lines>
|
||||
|
||||
{selectedLines}
|
||||
</TerminalCode>
|
||||
</TerminalContentContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function renderLines(lines: BufferEntry[], highlights?: KeywordHighlight[]) {
|
||||
if (!lines.length) {
|
||||
return (
|
||||
<Prompt key="cursor">
|
||||
$ <Cursor />
|
||||
</Prompt>
|
||||
);
|
||||
}
|
||||
|
||||
const result = lines.map(line => (
|
||||
<Fragment key={line.id}>
|
||||
{line.isCommand ? (
|
||||
<Prompt>${line.text.length > 0 ? ' ' : ''}</Prompt>
|
||||
) : null}
|
||||
{formatText(line.text, line.isCommand, highlights)}
|
||||
{line.isCurrent && line.isCommand ? <Cursor /> : null}
|
||||
<br />
|
||||
</Fragment>
|
||||
));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function highlightWords(content: string, highlight: KeywordHighlight) {
|
||||
const regex = new RegExp(`(${highlight.keywords.join('|')})`);
|
||||
|
||||
if (regex.test(content)) {
|
||||
const split = content.split(regex);
|
||||
|
||||
return split
|
||||
.map((item, index) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all odd occurrences are matches, the rest remain unchanged
|
||||
if (index % 2 === 0) {
|
||||
return <span key={index}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
key={`${highlight.key}-${index}`}
|
||||
style={{ color: highlight.color }}
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function formatText(
|
||||
source: string,
|
||||
isCommand: boolean,
|
||||
highlights?: KeywordHighlight[]
|
||||
) {
|
||||
let text = source;
|
||||
let comment;
|
||||
|
||||
const commentStartIndex = text.indexOf('#');
|
||||
if (commentStartIndex > -1) {
|
||||
text = source.substring(0, commentStartIndex);
|
||||
|
||||
comment = (
|
||||
<Comment>{source.substring(commentStartIndex, source.length)}</Comment>
|
||||
);
|
||||
}
|
||||
|
||||
const words = text.split(' ');
|
||||
const result = [];
|
||||
|
||||
outer: for (const [index, word] of words.entries()) {
|
||||
if (!isCommand && /(https?:\/\/\S+)/g.test(word)) {
|
||||
result.push(
|
||||
<Fragment key={index}>
|
||||
<a
|
||||
key={index}
|
||||
style={{ color: '#feaa01', textDecoration: 'underline' }}
|
||||
href={word}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{word}
|
||||
</a>{' '}
|
||||
</Fragment>
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (highlights) {
|
||||
for (const entry of highlights) {
|
||||
const highlightedWords = highlightWords(word, entry);
|
||||
if (highlightedWords) {
|
||||
result.push(
|
||||
<Word key={`${entry.key}-${index}`}>{highlightedWords} </Word>
|
||||
);
|
||||
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(<Word key={index}>{word} </Word>);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{result}
|
||||
{comment}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const Word = styled.span`
|
||||
user-select: none;
|
||||
color: ${props => props.theme.colors.light};
|
||||
`;
|
||||
|
||||
const Prompt = styled.span`
|
||||
user-select: none;
|
||||
color: rgb(204, 204, 204);
|
||||
`;
|
||||
|
||||
const Comment = styled.span`
|
||||
user-select: none;
|
||||
color: rgb(255, 255, 255, 0.4);
|
||||
`;
|
||||
|
||||
const Cursor = styled.span`
|
||||
display: inline-block;
|
||||
width: 6px;
|
||||
height: 15px;
|
||||
background: #ffffff;
|
||||
vertical-align: middle;
|
||||
`;
|
||||
|
||||
export const TerminalContentContainer = styled.div`
|
||||
background: #04162c;
|
||||
height: inherit;
|
||||
overflow-y: auto;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
`;
|
||||
|
||||
export const TerminalCode = styled.div`
|
||||
font-size: 12px;
|
||||
font-family:
|
||||
Menlo,
|
||||
DejaVu Sans Mono,
|
||||
Consolas,
|
||||
Lucida Console,
|
||||
monospace;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
margin: 10px 16px;
|
||||
position: relative;
|
||||
`;
|
||||
@@ -1,177 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { wait } from 'shared/utils/wait';
|
||||
|
||||
export interface BufferEntry {
|
||||
id: number;
|
||||
text: string;
|
||||
isCommand: boolean;
|
||||
isCurrent: boolean;
|
||||
}
|
||||
|
||||
type FrameFunction = () => Frame;
|
||||
|
||||
interface Frame {
|
||||
text: string;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
export interface TerminalLine {
|
||||
text?: string;
|
||||
isCommand: boolean;
|
||||
delay?: number;
|
||||
hasFinished?: () => boolean;
|
||||
frames?: FrameFunction[];
|
||||
}
|
||||
|
||||
export async function* createTerminalContent(
|
||||
lines: TerminalLine[],
|
||||
lineIndex: number
|
||||
): AsyncIterableIterator<BufferEntry[]> {
|
||||
let linePosition = 0;
|
||||
let frameIndex = 0;
|
||||
let hasFrame = false;
|
||||
|
||||
const buffer: BufferEntry[] = [];
|
||||
|
||||
if (lineIndex > 0) {
|
||||
for (let i = 0; i < lineIndex; i++) {
|
||||
buffer.push({
|
||||
id: i,
|
||||
text: lines[i].text,
|
||||
isCommand: lines[i].isCommand,
|
||||
isCurrent: i === lineIndex,
|
||||
});
|
||||
}
|
||||
|
||||
yield buffer;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (lineIndex < lines.length) {
|
||||
if (!lines[lineIndex].isCommand) {
|
||||
const delay = lines[lineIndex].delay;
|
||||
|
||||
if (!isNaN(delay)) {
|
||||
await wait(delay);
|
||||
|
||||
yield buffer;
|
||||
}
|
||||
|
||||
const frames = lines[lineIndex].frames;
|
||||
|
||||
if (!frames) {
|
||||
buffer.push({
|
||||
id: lineIndex,
|
||||
text: lines[lineIndex].text,
|
||||
isCommand: false,
|
||||
isCurrent: false,
|
||||
});
|
||||
|
||||
yield buffer;
|
||||
|
||||
linePosition = 0;
|
||||
lineIndex += 1;
|
||||
} else if (frameIndex < frames.length) {
|
||||
const frame = frames[frameIndex]();
|
||||
|
||||
if (frameIndex === 0 && !hasFrame) {
|
||||
hasFrame = true;
|
||||
buffer.push({
|
||||
id: lineIndex,
|
||||
text: frame.text,
|
||||
isCurrent: false,
|
||||
isCommand: false,
|
||||
});
|
||||
}
|
||||
|
||||
buffer[lineIndex].text = frame.text;
|
||||
|
||||
if (!isNaN(frame.delay)) {
|
||||
yield buffer;
|
||||
|
||||
await wait(frame.delay);
|
||||
|
||||
yield buffer;
|
||||
}
|
||||
|
||||
frameIndex += 1;
|
||||
} else {
|
||||
if (hasFrame && lines[lineIndex + 1]) {
|
||||
buffer[lineIndex].text = lines[lineIndex].text;
|
||||
|
||||
linePosition = 0;
|
||||
frameIndex = 0;
|
||||
lineIndex += 1;
|
||||
hasFrame = false;
|
||||
}
|
||||
|
||||
frameIndex = 0;
|
||||
}
|
||||
} else if (linePosition > lines[lineIndex].text.length) {
|
||||
buffer[lineIndex].isCurrent = lineIndex === lines.length - 1;
|
||||
linePosition = 0;
|
||||
|
||||
yield buffer;
|
||||
|
||||
await wait(300);
|
||||
|
||||
lineIndex += 1;
|
||||
} else {
|
||||
const delay = lines[lineIndex].delay;
|
||||
|
||||
if (!isNaN(delay)) {
|
||||
yield buffer;
|
||||
|
||||
await wait(delay);
|
||||
|
||||
yield buffer;
|
||||
}
|
||||
|
||||
if (linePosition === 0) {
|
||||
await wait(100);
|
||||
|
||||
buffer.push({
|
||||
id: lineIndex,
|
||||
text: '',
|
||||
isCommand: lines[lineIndex].isCommand,
|
||||
isCurrent: true,
|
||||
});
|
||||
|
||||
yield buffer;
|
||||
|
||||
await wait(600);
|
||||
}
|
||||
|
||||
buffer[lineIndex].text = lines[lineIndex].text.substring(
|
||||
0,
|
||||
linePosition
|
||||
);
|
||||
|
||||
linePosition += 1;
|
||||
}
|
||||
|
||||
yield buffer;
|
||||
} else {
|
||||
yield buffer;
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { AnimatedTerminal } from './AnimatedTerminal';
|
||||
export { TerminalColor } from './TerminalContent';
|
||||
@@ -1,24 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated Use `ButtonWithAddIcon` instead.
|
||||
*
|
||||
* TODO (bernardjkim): Remove once components in e repo have been updated.
|
||||
*/
|
||||
export { ButtonWithAddIcon as ButtonTextWithAddIcon } from '../ButtonWithAddIcon';
|
||||
@@ -332,7 +332,7 @@ function commonFeaturePossible(
|
||||
* Determines whether clipboard sharing is/should-be possible based on whether it's allowed by the acl
|
||||
* and whether it's supported by the browser.
|
||||
*/
|
||||
export function clipboardSharingPossible(
|
||||
function clipboardSharingPossible(
|
||||
clipboardSharingState: ClipboardSharingState
|
||||
): boolean {
|
||||
return commonFeaturePossible(clipboardSharingState);
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
import {
|
||||
KeywordHighlight,
|
||||
TerminalColor,
|
||||
} from 'shared/components/AnimatedTerminal/TerminalContent';
|
||||
import { Language } from 'shared/components/Editor/Language';
|
||||
import { Tabs } from 'shared/components/Editor/Tabs';
|
||||
import {
|
||||
WindowCode,
|
||||
WindowContainer,
|
||||
WindowContentContainer,
|
||||
WindowTitleBar,
|
||||
} from 'shared/components/Window';
|
||||
|
||||
import { File, FileProps } from './File';
|
||||
|
||||
interface EditorProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function Editor(props: React.PropsWithChildren<EditorProps>) {
|
||||
const [activeTabIndex, setActiveTabIndex] = useState(0);
|
||||
|
||||
const files = React.Children.map(
|
||||
props.children,
|
||||
(child: React.ReactElement<FileProps>) => {
|
||||
if (child.type === File) {
|
||||
return {
|
||||
name: child.props.name,
|
||||
content: child.props.code,
|
||||
language: child.props.language,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
).filter(Boolean);
|
||||
|
||||
const tabs = files.map(file => file.name);
|
||||
|
||||
const { content, language } = files[activeTabIndex];
|
||||
|
||||
const parsed = parse(content, language);
|
||||
|
||||
const lineNumbers = [];
|
||||
if (content) {
|
||||
const numberOfLines = content.split('\n').length;
|
||||
|
||||
for (let i = 0; i <= numberOfLines; i++) {
|
||||
lineNumbers.push(
|
||||
<LineNumber
|
||||
key={i}
|
||||
data-line-number={i + 1}
|
||||
active={i === numberOfLines}
|
||||
/>
|
||||
);
|
||||
}
|
||||
} else {
|
||||
lineNumbers.push(<LineNumber key={0} data-line-number={1} active />);
|
||||
}
|
||||
|
||||
return (
|
||||
<WindowContainer>
|
||||
<WindowTitleBar title={props.title} />
|
||||
|
||||
<Tabs
|
||||
items={tabs}
|
||||
activeIndex={activeTabIndex}
|
||||
onSelect={setActiveTabIndex}
|
||||
/>
|
||||
|
||||
<WindowContentContainer style={{ height: 585 }}>
|
||||
<WindowCode style={{ display: 'flex' }}>
|
||||
<LineNumbers>{lineNumbers}</LineNumbers>
|
||||
<Code>
|
||||
{parsed}
|
||||
<ActiveLine>
|
||||
<Cursor />
|
||||
</ActiveLine>
|
||||
</Code>
|
||||
</WindowCode>
|
||||
</WindowContentContainer>
|
||||
</WindowContainer>
|
||||
);
|
||||
}
|
||||
|
||||
function parse(code: string, language: Language) {
|
||||
switch (language) {
|
||||
case Language.YAML:
|
||||
return parseYAML(code);
|
||||
default:
|
||||
throw new Error('Language not supported');
|
||||
}
|
||||
}
|
||||
|
||||
function parseYAML(code: string) {
|
||||
if (!code) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const highlights = [
|
||||
{
|
||||
key: 'string',
|
||||
keywords: [`'\\*'`],
|
||||
color: TerminalColor.Keyword,
|
||||
},
|
||||
{
|
||||
key: 'certificate',
|
||||
match: /(-----.*?-----)/,
|
||||
color: TerminalColor.Punctuation,
|
||||
},
|
||||
];
|
||||
|
||||
const lines = code.split('\n');
|
||||
|
||||
const result = [];
|
||||
for (const [index, line] of lines.entries()) {
|
||||
const highlightSemicolonMultiline = highlight(
|
||||
line,
|
||||
': |',
|
||||
index,
|
||||
highlights
|
||||
);
|
||||
if (highlightSemicolonMultiline) {
|
||||
result.push(highlightSemicolonMultiline);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
const highlightSemicolon = highlight(line, ':', index, highlights);
|
||||
if (highlightSemicolon) {
|
||||
result.push(highlightSemicolon);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line) {
|
||||
result.push(<div key={index}>{highlightValue(line, highlights)}</div>);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push(<div key={index}> </div>);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function highlight(
|
||||
code: string,
|
||||
symbol: string,
|
||||
index: number,
|
||||
highlights: KeywordHighlight[]
|
||||
) {
|
||||
if (!code.includes(symbol)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const symbolIndex = code.indexOf(symbol);
|
||||
|
||||
let value = code.substring(symbolIndex + symbol.length, code.length);
|
||||
|
||||
return (
|
||||
<div key={index}>
|
||||
<Keyword>{code.substring(0, symbolIndex)}</Keyword>
|
||||
<Punctuation>{symbol}</Punctuation>
|
||||
{highlightValue(value, highlights)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function highlightValue(content: string, highlights: KeywordHighlight[]) {
|
||||
for (const entry of highlights) {
|
||||
if (entry.match && entry.match.test(content)) {
|
||||
const split = content.split(entry.match);
|
||||
|
||||
return split
|
||||
.map((item, index) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all odd occurrences are matches, the rest remain unchanged
|
||||
if (index % 2 === 0) {
|
||||
return <span key={index}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={`${entry.key}-${index}`} style={{ color: entry.color }}>
|
||||
{item}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
const words = content.split(' ');
|
||||
const result = [];
|
||||
|
||||
outer: for (const [index, word] of words.entries()) {
|
||||
for (const entry of highlights) {
|
||||
if (entry.keywords) {
|
||||
const highlightedWords = highlightWord(word, entry);
|
||||
if (highlightedWords) {
|
||||
result.push(
|
||||
<span key={`${entry.key}-${index}`}>{highlightedWords} </span>
|
||||
);
|
||||
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.push(<span key={index}>{word} </span>);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function highlightWord(content: string, highlight: KeywordHighlight) {
|
||||
const regex = new RegExp(`(${highlight.keywords.join('|')})`);
|
||||
|
||||
if (regex.test(content)) {
|
||||
const split = content.split(regex);
|
||||
|
||||
return split
|
||||
.map((item, index) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all odd occurrences are matches, the rest remain unchanged
|
||||
if (index % 2 === 0) {
|
||||
return <span key={index}>{item}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span
|
||||
key={`${highlight.key}-${index}`}
|
||||
style={{ color: highlight.color }}
|
||||
>
|
||||
{item}
|
||||
</span>
|
||||
);
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const Keyword = styled.span`
|
||||
color: #d4656b;
|
||||
`;
|
||||
|
||||
const Punctuation = styled.span`
|
||||
color: #81ceee;
|
||||
`;
|
||||
|
||||
const blink = keyframes`
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Cursor = styled.span`
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
height: 15px;
|
||||
background: #ffffff;
|
||||
vertical-align: middle;
|
||||
animation: ${blink} 1.5s steps(2) infinite;
|
||||
`;
|
||||
|
||||
const LineNumbers = styled.div`
|
||||
user-select: none;
|
||||
width: 55px;
|
||||
`;
|
||||
|
||||
interface LineNumberProps {
|
||||
active?: boolean;
|
||||
}
|
||||
|
||||
const LineNumber = styled.div<LineNumberProps>`
|
||||
background: ${p => (p.active ? 'rgba(0, 0, 0, 0.3)' : 'none')};
|
||||
color: ${p =>
|
||||
p.active ? 'rgba(255, 255, 255, 0.6)' : 'rgba(255, 255, 255, 0.3)'};
|
||||
text-align: right;
|
||||
padding-right: 20px;
|
||||
|
||||
&:before {
|
||||
content: attr(data-line-number);
|
||||
}
|
||||
`;
|
||||
|
||||
const Code = styled.div`
|
||||
width: 100%;
|
||||
color: white;
|
||||
`;
|
||||
|
||||
const ActiveLine = styled.div`
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
width: 100%;
|
||||
`;
|
||||
@@ -1,30 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Language } from './Language';
|
||||
|
||||
export interface FileProps {
|
||||
name: string;
|
||||
language: Language;
|
||||
code: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
export function File(props: FileProps) {
|
||||
return null;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export enum Language {
|
||||
YAML,
|
||||
}
|
||||
@@ -41,12 +41,12 @@ export function Tabs(props: TabsProps) {
|
||||
return <TabsContainer>{tabs}</TabsContainer>;
|
||||
}
|
||||
|
||||
export const TabsContainer = styled.div`
|
||||
const TabsContainer = styled.div`
|
||||
background: #0a102c;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const Tab = styled.div<{ active: boolean }>`
|
||||
const Tab = styled.div<{ active: boolean }>`
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
padding: 8px 20px 10px 15px;
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { File } from './File';
|
||||
export { Editor } from './Editor';
|
||||
export { Language } from './Language';
|
||||
@@ -100,15 +100,15 @@ export const DetailsTab = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const Title = styled(Text)`
|
||||
const Title = styled(Text)`
|
||||
font-weight: bold;
|
||||
`;
|
||||
|
||||
export const Description = styled(Text)`
|
||||
const Description = styled(Text)`
|
||||
font-size: ${p => p.theme.fontSizes[1]}px;
|
||||
`;
|
||||
|
||||
export const Feature = styled(Box)<{ $isSliding?: boolean; $active?: boolean }>`
|
||||
const Feature = styled(Box)<{ $isSliding?: boolean; $active?: boolean }>`
|
||||
height: var(--feature-height);
|
||||
|
||||
line-height: 20px;
|
||||
|
||||
@@ -20,9 +20,4 @@ import FieldSelect from './FieldSelect';
|
||||
|
||||
export default FieldSelect;
|
||||
export { FieldSelect, FieldSelectAsync } from './FieldSelect';
|
||||
export { resolveUndefinedOptions } from './shared';
|
||||
|
||||
export {
|
||||
FieldSelectCreatable,
|
||||
FieldSelectCreatableAsync,
|
||||
} from './FieldSelectCreatable';
|
||||
export { FieldSelectCreatable } from './FieldSelectCreatable';
|
||||
|
||||
@@ -33,12 +33,6 @@ import { Rule } from '../Validation/rules';
|
||||
|
||||
export const defaultRule = () => () => ({ valid: true });
|
||||
|
||||
export const LabelTip = ({ text }) => (
|
||||
<span
|
||||
css={{ fontWeight: 'normal', textTransform: 'none' }}
|
||||
>{` - ${text}`}</span>
|
||||
);
|
||||
|
||||
type FieldSelectWrapperPropsBase<Opt, IsMulti extends boolean> = {
|
||||
label?: string;
|
||||
toolTipContent?: React.ReactNode;
|
||||
|
||||
@@ -94,7 +94,7 @@ export function FileTransfer(props: FileTransferProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function FileTransferDialog(
|
||||
function FileTransferDialog(
|
||||
props: Pick<FileTransferProps, 'transferHandlers' | 'errorText'> & {
|
||||
openedDialog: FileTransferDialogDirection;
|
||||
onCloseDialog(isAnyTransferInProgress: boolean): void;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
|
||||
export { FileTransfer } from './FileTransfer';
|
||||
export { FileTransferContainer } from './FileTransferContainer';
|
||||
export {
|
||||
FileTransferContextProvider,
|
||||
useFileTransferContext,
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Flex } from 'design';
|
||||
import { StyledArrowBtn } from 'design/DataTable/Pager/StyledPager';
|
||||
import { StyledPanel } from 'design/DataTable/StyledTable';
|
||||
import { CircleArrowLeft, CircleArrowRight } from 'design/Icon';
|
||||
|
||||
export function SearchPagination({ prevPage, nextPage }: Props) {
|
||||
return (
|
||||
<StyledPanel>
|
||||
<Flex justifyContent="flex-end" width="100%">
|
||||
<Flex alignItems="center" mr={2}></Flex>
|
||||
<Flex>
|
||||
<StyledArrowBtn
|
||||
onClick={prevPage}
|
||||
title="Previous page"
|
||||
disabled={!prevPage}
|
||||
mx={0}
|
||||
>
|
||||
<CircleArrowLeft />
|
||||
</StyledArrowBtn>
|
||||
<StyledArrowBtn
|
||||
ml={0}
|
||||
onClick={nextPage}
|
||||
title="Next page"
|
||||
disabled={!nextPage}
|
||||
>
|
||||
<CircleArrowRight />
|
||||
</StyledArrowBtn>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</StyledPanel>
|
||||
);
|
||||
}
|
||||
|
||||
type Props = {
|
||||
prevPage?: () => void;
|
||||
nextPage?: () => void;
|
||||
};
|
||||
@@ -17,4 +17,3 @@
|
||||
*/
|
||||
|
||||
export * from './SearchPanel';
|
||||
export * from './SearchPagination';
|
||||
|
||||
@@ -30,7 +30,7 @@ import { CreatableProps as ReactSelectCreatableProps } from 'react-select/creata
|
||||
|
||||
export type SelectSize = 'large' | 'medium' | 'small';
|
||||
|
||||
export type CommonProps<Opt, IsMulti extends boolean> = {
|
||||
type CommonProps<Opt, IsMulti extends boolean> = {
|
||||
size?: SelectSize;
|
||||
hasError?: boolean;
|
||||
/**
|
||||
|
||||
@@ -376,7 +376,7 @@ export function shouldWarnResourceStatus(
|
||||
return status === 'mixed' || status === 'unhealthy';
|
||||
}
|
||||
|
||||
export const StyledUl = styled.ul`
|
||||
const StyledUl = styled.ul`
|
||||
margin: 0;
|
||||
padding-left: ${p => p.theme.space[4]}px;
|
||||
padding-bottom: ${p => p.theme.space[1]}px;
|
||||
|
||||
@@ -175,7 +175,7 @@ export function makeUnifiedResourceViewItemDesktop(
|
||||
};
|
||||
}
|
||||
|
||||
export function makeUnifiedResourceViewItemLinuxDesktop(
|
||||
function makeUnifiedResourceViewItemLinuxDesktop(
|
||||
resource: UnifiedResourceDesktop,
|
||||
ui: UnifiedResourceUi
|
||||
): UnifiedResourceViewItem {
|
||||
@@ -195,7 +195,7 @@ export function makeUnifiedResourceViewItemLinuxDesktop(
|
||||
};
|
||||
}
|
||||
|
||||
export function makeUnifiedResourceViewItemUserGroup(
|
||||
function makeUnifiedResourceViewItemUserGroup(
|
||||
resource: UnifiedResourceUserGroup,
|
||||
ui: UnifiedResourceUi
|
||||
): UnifiedResourceViewItem {
|
||||
@@ -213,7 +213,7 @@ export function makeUnifiedResourceViewItemUserGroup(
|
||||
};
|
||||
}
|
||||
|
||||
export function makeUnifiedResourceViewItemGitServer(
|
||||
function makeUnifiedResourceViewItemGitServer(
|
||||
resource: UnifiedResourceGitServer,
|
||||
ui: UnifiedResourceUi
|
||||
): UnifiedResourceViewItem {
|
||||
|
||||
@@ -17,4 +17,3 @@
|
||||
*/
|
||||
|
||||
export { UserDisplayName } from './UserDisplayName';
|
||||
export type { UserDisplayNameLayout } from './UserDisplayName';
|
||||
|
||||
@@ -353,7 +353,7 @@ const isIamRoleNameValid = (roleName: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
export interface EmailValidationResult extends ValidationResult {
|
||||
interface EmailValidationResult extends ValidationResult {
|
||||
kind?: 'empty' | 'invalid';
|
||||
}
|
||||
|
||||
@@ -487,7 +487,7 @@ const requiredAll =
|
||||
};
|
||||
|
||||
/** A result of the {@link arrayOf} validation rule. */
|
||||
export type ArrayValidationResult<R = ValidationResult> = ValidationResult & {
|
||||
type ArrayValidationResult<R = ValidationResult> = ValidationResult & {
|
||||
/** Results of validating each separate item. */
|
||||
results: R[];
|
||||
};
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { WindowContainer, WindowContentContainer } from './WindowComponents';
|
||||
import { WindowTitleBar } from './WindowTitleBar';
|
||||
|
||||
interface WindowProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function Window(props: React.PropsWithChildren<WindowProps>) {
|
||||
return (
|
||||
<WindowContainer>
|
||||
<WindowTitleBar title={props.title} />
|
||||
|
||||
<WindowContentContainer>{props.children}</WindowContentContainer>
|
||||
</WindowContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const WindowContainer = styled.div`
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.43);
|
||||
`;
|
||||
|
||||
export const WindowTitleBarContainer = styled.div`
|
||||
background: #040b1d;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-top-left-radius: 5px;
|
||||
border-top-right-radius: 5px;
|
||||
`;
|
||||
|
||||
export const WindowTitleBarButtons = styled.div`
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10px;
|
||||
transform: translate(0, -50%);
|
||||
`;
|
||||
|
||||
export const WindowTitleBarButton = styled.div`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
export const WindowContentContainer = styled.div`
|
||||
background: #04162c;
|
||||
height: var(--content-height, 660px);
|
||||
overflow-y: auto;
|
||||
border-bottom-left-radius: 5px;
|
||||
border-bottom-right-radius: 5px;
|
||||
`;
|
||||
|
||||
export const WindowCode = styled.div`
|
||||
font-size: 12px;
|
||||
font-family:
|
||||
Menlo,
|
||||
DejaVu Sans Mono,
|
||||
Consolas,
|
||||
Lucida Console,
|
||||
monospace;
|
||||
line-height: 20px;
|
||||
white-space: pre-wrap;
|
||||
`;
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {
|
||||
WindowTitleBarButton,
|
||||
WindowTitleBarButtons,
|
||||
WindowTitleBarContainer,
|
||||
} from './WindowComponents';
|
||||
|
||||
interface WindowTitleBarProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
export function WindowTitleBar(props: WindowTitleBarProps) {
|
||||
return (
|
||||
<WindowTitleBarContainer
|
||||
css={`
|
||||
color: ${props => props.theme.colors.light};
|
||||
`}
|
||||
>
|
||||
<WindowTitleBarButtons>
|
||||
<WindowTitleBarButton style={{ backgroundColor: '#f95e57' }} />
|
||||
<WindowTitleBarButton style={{ backgroundColor: '#fbbe2e' }} />
|
||||
<WindowTitleBarButton style={{ backgroundColor: '#31c842' }} />
|
||||
</WindowTitleBarButtons>
|
||||
{props.title}
|
||||
</WindowTitleBarContainer>
|
||||
);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { WindowTitleBar } from './WindowTitleBar';
|
||||
export { Window } from './Window';
|
||||
export {
|
||||
WindowContentContainer,
|
||||
WindowCode,
|
||||
WindowContainer,
|
||||
WindowTitleBarButton,
|
||||
WindowTitleBarButtons,
|
||||
WindowTitleBarContainer,
|
||||
} from './WindowComponents';
|
||||
@@ -43,7 +43,7 @@ function makeTestResources(
|
||||
}));
|
||||
}
|
||||
|
||||
export function newDOMAbortError() {
|
||||
function newDOMAbortError() {
|
||||
return new DOMException('Aborted', 'AbortError');
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ type EventMap = {
|
||||
'terminal.webauthn': [string];
|
||||
};
|
||||
|
||||
export enum LogType {
|
||||
enum LogType {
|
||||
OFF = 'OFF',
|
||||
ERROR = 'ERROR',
|
||||
WARN = 'WARN',
|
||||
|
||||
@@ -161,7 +161,7 @@ export enum Severity {
|
||||
/**
|
||||
* @throws {Error} if an invalid severity is passed
|
||||
*/
|
||||
export function toSeverity(severity: number): Severity {
|
||||
function toSeverity(severity: number): Severity {
|
||||
if (severity === Severity.Info) {
|
||||
return Severity.Info;
|
||||
} else if (severity === Severity.Warning) {
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"name": "@gravitational/shared",
|
||||
"version": "1.0.0",
|
||||
"description": "Gravitational library of shared components",
|
||||
"main": "index.js",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -17,6 +17,5 @@
|
||||
*/
|
||||
|
||||
import { processRedirectUri } from './processRedirectUri';
|
||||
import { validateClientRedirect } from './urlValidation';
|
||||
|
||||
export { processRedirectUri, validateClientRedirect };
|
||||
export { processRedirectUri };
|
||||
|
||||
@@ -137,7 +137,7 @@ export type ResourceId = {
|
||||
};
|
||||
|
||||
// ResourceDetails holds optional details for a resource.
|
||||
export type ResourceDetails = {
|
||||
type ResourceDetails = {
|
||||
// hostname is the resource hostname.
|
||||
// TODO(mdwn): Remove hostname as it's no longer used.
|
||||
hostname?: string;
|
||||
|
||||
@@ -134,8 +134,6 @@ export const formatDatabaseInfo = (type: DbType, protocol: DbProtocol) => {
|
||||
}
|
||||
};
|
||||
|
||||
export type DatabaseInfo = ReturnType<typeof formatDatabaseInfo>;
|
||||
|
||||
export type DbNameRequirement = 'unsupported' | 'optional' | 'required';
|
||||
|
||||
const dbNameRequirementsByProtocol: Record<DbProtocol, DbNameRequirement> = {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export type MCPServerConfig = {
|
||||
type MCPServerConfig = {
|
||||
command: string;
|
||||
args: string[];
|
||||
};
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* pipe takes an array of unary functions as an argument. It returns a function that accepts a value
|
||||
* that's going to be passed through the supplied functions.
|
||||
*
|
||||
* @example
|
||||
* // Without pipe.
|
||||
* const add1ThenDouble = (x) => double(add1(x));
|
||||
*
|
||||
* // With pipe.
|
||||
* const add1ThenDouble = pipe(add1, double);
|
||||
*/
|
||||
export const pipe =
|
||||
<PipeSubject>(...fns: Array<(pipeSubject: PipeSubject) => PipeSubject>) =>
|
||||
(x: PipeSubject) =>
|
||||
fns.reduce((v, f) => f(v), x);
|
||||
@@ -2,7 +2,6 @@
|
||||
"name": "@gravitational/teleport",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "pnpm build-wasm && vite",
|
||||
"build": "pnpm build-wasm && vite build",
|
||||
@@ -46,6 +45,7 @@
|
||||
"devDependencies": {
|
||||
"@gravitational/build": "workspace:*",
|
||||
"@gravitational/teleport": "workspace:*",
|
||||
"ace-builds": "1.44.0",
|
||||
"babel-plugin-transform-import-meta": "2.3.3",
|
||||
"babel-plugin-transform-vite-meta-env": "^1.0.3",
|
||||
"babel-plugin-transform-vite-meta-hot": "^1.0.0",
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2024 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Compatibility aliases to prevent e/ from breaking.
|
||||
// TODO(bl-nero): Remove these once e/ stops referring to them.
|
||||
import { Account } from './Account';
|
||||
|
||||
export type { EnterpriseComponentProps } from './Account';
|
||||
|
||||
export default Account;
|
||||
@@ -39,31 +39,3 @@ export const dummyPasskey: MfaDevice = {
|
||||
type: 'webauthn',
|
||||
usage: 'passwordless',
|
||||
};
|
||||
|
||||
/** A dummy MFA device for test purposes. */
|
||||
export const dummyAuthenticatorApp: MfaDevice = {
|
||||
id: '4',
|
||||
description: 'Authenticator App',
|
||||
name: 'iphone 12',
|
||||
registeredDate: new Date(1623722252),
|
||||
lastUsedDate: new Date(1623981452),
|
||||
type: 'totp',
|
||||
usage: 'mfa',
|
||||
};
|
||||
|
||||
/**
|
||||
* Repeats devices twice to make sure we support multiple devices of the same
|
||||
* type and purpose.
|
||||
*/
|
||||
function twice(arr) {
|
||||
return [...arr, ...arr];
|
||||
}
|
||||
|
||||
/** Dummy devices for testing purposes. */
|
||||
export const deviceCases: Record<string, MfaDevice[]> = {
|
||||
all: twice([dummyAuthenticatorApp, dummyHardwareDevice, dummyPasskey]),
|
||||
authApps: twice([dummyAuthenticatorApp]),
|
||||
mfaDevices: twice([dummyHardwareDevice]),
|
||||
passkeys: twice([dummyPasskey]),
|
||||
none: [],
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ import cfg from 'teleport/config';
|
||||
import { preferencesHeadings } from './Preferences';
|
||||
import { securityHeadings } from './SecuritySettings';
|
||||
|
||||
export interface Heading {
|
||||
interface Heading {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { AccountPage, Account } from './Account';
|
||||
export { AccountPage } from './Account';
|
||||
|
||||
@@ -75,10 +75,7 @@ export default function EventList(props: Props) {
|
||||
);
|
||||
}
|
||||
|
||||
export const renderActionCell = (
|
||||
event: Event,
|
||||
onShowDetails: (e: Event) => void
|
||||
) => (
|
||||
const renderActionCell = (event: Event, onShowDetails: (e: Event) => void) => (
|
||||
<Cell align="right">
|
||||
<Flex gap={2} justifyContent="flex-end">
|
||||
<ViewInPolicyButton event={event} />
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { ButtonBorder } from 'design';
|
||||
import { Cell } from 'design/DataTable';
|
||||
import { displayDateTime } from 'design/datetime';
|
||||
|
||||
import { Event } from 'teleport/services/audit';
|
||||
|
||||
export const ActionCell = props => {
|
||||
const { rowIndex, onViewDetails, data } = props;
|
||||
const event: Event = data[rowIndex];
|
||||
|
||||
function onClick() {
|
||||
onViewDetails(event);
|
||||
}
|
||||
return (
|
||||
<Cell align="right">
|
||||
<ButtonBorder size="small" onClick={onClick} width="87px">
|
||||
Details
|
||||
</ButtonBorder>
|
||||
</Cell>
|
||||
);
|
||||
};
|
||||
|
||||
export const TimeCell = props => {
|
||||
const { rowIndex, data } = props;
|
||||
const { time } = data[rowIndex];
|
||||
return <Cell style={{ minWidth: '120px' }}>{displayDateTime(time)}</Cell>;
|
||||
};
|
||||
|
||||
export function DescCell(props) {
|
||||
const { rowIndex, data } = props;
|
||||
const { message } = data[rowIndex];
|
||||
return <Cell style={{ wordBreak: 'break-word' }}>{message}</Cell>;
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import Text from 'design/Text/Text';
|
||||
|
||||
import { Event, EventCode, eventCodes } from 'teleport/services/audit';
|
||||
|
||||
export const EventIconMap: Record<EventCode, any> = {
|
||||
const EventIconMap: Record<EventCode, any> = {
|
||||
[eventCodes.AUTH_ATTEMPT_FAILURE]: Icons.Info,
|
||||
[eventCodes.EXEC_FAILURE]: Icons.Cli,
|
||||
[eventCodes.EXEC]: Icons.Cli,
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { Audit, AuditContainer } from './Audit';
|
||||
export { AuditContainer } from './Audit';
|
||||
|
||||
@@ -193,7 +193,7 @@ export function LocalConnectorTile({
|
||||
);
|
||||
}
|
||||
|
||||
export const ConnectorBox = styled(Box)<{ disabled?: boolean }>`
|
||||
const ConnectorBox = styled(Box)<{ disabled?: boolean }>`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
@@ -46,9 +46,6 @@ import DeleteConnectorDialog from './DeleteConnectorDialog';
|
||||
import EmptyList from './EmptyList';
|
||||
import templates from './templates';
|
||||
|
||||
export const description =
|
||||
'Auth connectors allow Teleport to authenticate users via an external identity source such as Okta, Microsoft Entra ID, GitHub, etc. This authentication method is commonly known as single sign-on (SSO).';
|
||||
|
||||
/**
|
||||
* AuthConnectorsContainer is the container for the Auth Connectors feature and handles routing to the relevant page based on the URL.
|
||||
*/
|
||||
|
||||
@@ -16,4 +16,4 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { AuthConnectors, AuthConnectorsContainer } from './AuthConnectors';
|
||||
export { AuthConnectorsContainer } from './AuthConnectors';
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Box, Button, Subtitle1 } from 'design';
|
||||
import { Button } from 'design';
|
||||
|
||||
import { FeatureHeader } from 'teleport/components/Layout';
|
||||
|
||||
@@ -35,23 +35,6 @@ export const ResponsiveFeatureHeader = styled(FeatureHeader)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const MobileDescription = styled(Subtitle1)`
|
||||
margin-bottom: ${p => p.theme.space[3]}px;
|
||||
@media screen and (min-width: ${p => p.theme.breakpoints.tablet}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const DesktopDescription = styled(Box)`
|
||||
margin-left: ${p => p.theme.space[4]}px;
|
||||
width: 240px;
|
||||
color: ${p => p.theme.colors.text.main};
|
||||
flex-shrink: 0;
|
||||
@media screen and (max-width: ${p => p.theme.breakpoints.tablet}) {
|
||||
display: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ResponsiveAddButton = styled(Button)`
|
||||
width: 240px;
|
||||
@media screen and (max-width: ${p => p.theme.breakpoints.tablet}) {
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Flex } from 'design';
|
||||
|
||||
import { ButtonLockedFeature } from 'teleport/components/ButtonLockedFeature';
|
||||
|
||||
export const LockedFeatureContainer = styled(Flex)`
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
min-width: 224px;
|
||||
`;
|
||||
|
||||
export const LockedFeatureButton = styled(ButtonLockedFeature)`
|
||||
position: absolute;
|
||||
width: 80%;
|
||||
right: 10%;
|
||||
bottom: -10px;
|
||||
|
||||
@media screen and (max-width: ${props => props.theme.breakpoints.tablet}) {
|
||||
width: 100%;
|
||||
right: 1px;
|
||||
}
|
||||
`;
|
||||
@@ -150,7 +150,7 @@ const TitleContainer = styled(Flex)`
|
||||
overflow: hidden;
|
||||
`;
|
||||
|
||||
export const TitleText = styled(Text).attrs({
|
||||
const TitleText = styled(Text).attrs({
|
||||
as: 'h2',
|
||||
typography: 'h2',
|
||||
})`
|
||||
|
||||
@@ -210,7 +210,7 @@ const TitleContainer = styled(Flex)`
|
||||
min-height: ${p => p.theme.space[8]}px;
|
||||
`;
|
||||
|
||||
export const TitleText = styled(Text).attrs({
|
||||
const TitleText = styled(Text).attrs({
|
||||
as: 'h2',
|
||||
typography: 'h2',
|
||||
})``;
|
||||
|
||||
@@ -19,5 +19,7 @@
|
||||
import { getBotInstance } from 'teleport/services/bot/bot';
|
||||
import { createQueryHook } from 'teleport/services/queryHelpers';
|
||||
|
||||
export const { queryKey: GetBotInstanceQueryKey, useQuery: useGetBotInstance } =
|
||||
createQueryHook(['bot_instance', 'get'], getBotInstance);
|
||||
export const { useQuery: useGetBotInstance } = createQueryHook(
|
||||
['bot_instance', 'get'],
|
||||
getBotInstance
|
||||
);
|
||||
|
||||
@@ -481,7 +481,7 @@ function stripWildcard(labels: KubernetesLabel[]) {
|
||||
}
|
||||
|
||||
const labelNameRegex = /^([a-z0-9:]+|\*)$/; // 1-n alphanumerics or colon, or a single asterisk
|
||||
export const requireValidLabelName: Rule = value => () => {
|
||||
const requireValidLabelName: Rule = value => () => {
|
||||
const match = labelNameRegex.test(value.trim());
|
||||
return {
|
||||
valid: match,
|
||||
@@ -489,7 +489,7 @@ export const requireValidLabelName: Rule = value => () => {
|
||||
};
|
||||
};
|
||||
|
||||
export const requireValidLabelValue: Rule = value => () => {
|
||||
const requireValidLabelValue: Rule = value => () => {
|
||||
const trimmed = value.trim();
|
||||
return {
|
||||
valid: trimmed.length > 0,
|
||||
|
||||
@@ -32,7 +32,7 @@ export function makeGhaWorkflow(params: {
|
||||
);
|
||||
}
|
||||
|
||||
export const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow which enrolls with Teleport in
|
||||
const GHA_WORKFLOW = `# This file contains a GitHub Actions workflow which enrolls with Teleport in
|
||||
# order to access a Kubernetes cluster using kubectl or other tools compatible
|
||||
# with kubeconfig, such as Helm.
|
||||
|
||||
|
||||
@@ -17,4 +17,3 @@
|
||||
*/
|
||||
|
||||
export { GitHubActionsSsh as default } from './GitHubActionsSsh';
|
||||
export { ConnectGitHub as ConfigureBot } from './ConnectGitHub';
|
||||
|
||||
@@ -208,7 +208,7 @@ export type Rule = {
|
||||
actor: string;
|
||||
};
|
||||
|
||||
export const defaultRule: Rule = {
|
||||
const defaultRule: Rule = {
|
||||
workflowName: '',
|
||||
environment: '',
|
||||
ref: '',
|
||||
|
||||
@@ -24,7 +24,7 @@ import cfg from 'teleport/config';
|
||||
|
||||
import { FlowStepProps } from './GuidedFlow';
|
||||
|
||||
export type ButtonState = {
|
||||
type ButtonState = {
|
||||
disabled?: boolean;
|
||||
hidden?: boolean;
|
||||
};
|
||||
|
||||
@@ -95,10 +95,3 @@ export function makeKubernetesAccessChecker(selected: KubernetesLabel[]) {
|
||||
}
|
||||
|
||||
export type KubernetesLabel = { name: string; values: string[] };
|
||||
|
||||
export type KubernetesResourceRule = {
|
||||
kind: string;
|
||||
name: string;
|
||||
namespace: string;
|
||||
verbs: string[];
|
||||
};
|
||||
|
||||
@@ -23,13 +23,9 @@ import styled, { useTheme } from 'styled-components';
|
||||
import { Box, ButtonPrimary, Flex, H1, Image, Text } from 'design';
|
||||
import { ResourceIcon } from 'design/ResourceIcon';
|
||||
import {
|
||||
Description,
|
||||
DetailsTab,
|
||||
Feature,
|
||||
FeatureContainer,
|
||||
FeatureProps,
|
||||
FeatureSlider,
|
||||
Title,
|
||||
} from 'shared/components/EmptyState/EmptyState';
|
||||
|
||||
import { DisplayTile } from 'teleport/Bots/Add/AddBotsPicker';
|
||||
@@ -128,30 +124,12 @@ export function EmptyState() {
|
||||
);
|
||||
}
|
||||
|
||||
export const EliminateSecrets = ({
|
||||
active,
|
||||
onClick,
|
||||
isSliding,
|
||||
}: FeatureProps) => {
|
||||
return (
|
||||
<Feature $active={active} onClick={onClick} $isSliding={isSliding}>
|
||||
<Title>
|
||||
Eliminate secrets and shared credentials from CI/CD workflows.
|
||||
</Title>
|
||||
<Description>
|
||||
Teleport Machine ID replaces passwords, API, and static keys with
|
||||
short-lived SSH and x.509 certificates.
|
||||
</Description>
|
||||
</Feature>
|
||||
);
|
||||
};
|
||||
|
||||
const eliminateSecretsImages = {
|
||||
light: elimiateSecretsLightImage,
|
||||
dark: elimiateSecretsImage,
|
||||
};
|
||||
|
||||
export const EliminateSecretsPreview = () => {
|
||||
const EliminateSecretsPreview = () => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<PreviewBox includeShadow>
|
||||
@@ -165,7 +143,7 @@ const controlWorkflowsImages = {
|
||||
dark: controlWorkflowsDarkImage,
|
||||
};
|
||||
|
||||
export const ControlWorkflowsPreview = () => {
|
||||
const ControlWorkflowsPreview = () => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<PreviewBox includeShadow>
|
||||
@@ -208,7 +186,7 @@ const integrationsBottom = [
|
||||
},
|
||||
];
|
||||
|
||||
export const BotTiles = () => {
|
||||
const BotTiles = () => {
|
||||
return (
|
||||
<PreviewBox>
|
||||
<Flex>
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export * from './Bots';
|
||||
@@ -22,7 +22,7 @@ import { createQueryHook } from 'teleport/services/queryHelpers';
|
||||
export const { createQueryKey: createGetBotQueryKey, useQuery: useGetBot } =
|
||||
createQueryHook(['bot', 'get'], getBot);
|
||||
|
||||
export const {
|
||||
createQueryKey: createListBotTokensQueryKey,
|
||||
useQuery: useListBotTokens,
|
||||
} = createQueryHook(['bot', 'token', 'list'], listBotTokens);
|
||||
export const { useQuery: useListBotTokens } = createQueryHook(
|
||||
['bot', 'token', 'list'],
|
||||
listBotTokens
|
||||
);
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
*/
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
import { Attempt } from 'shared/hooks/useAttemptNext';
|
||||
|
||||
import { FlatBot } from 'teleport/services/bot/types';
|
||||
|
||||
export type BotOptionsCellProps = {
|
||||
@@ -42,13 +40,6 @@ export type BotListProps = {
|
||||
setSelectedBot: Dispatch<SetStateAction<FlatBot>>;
|
||||
};
|
||||
|
||||
export type DeleteBotProps = {
|
||||
attempt: Attempt;
|
||||
name: string;
|
||||
onClose: () => void;
|
||||
onDelete: () => void;
|
||||
};
|
||||
|
||||
export type ViewBotProps = {
|
||||
bot: FlatBot;
|
||||
onClose: () => void;
|
||||
@@ -58,13 +49,3 @@ export enum BotFlowType {
|
||||
GitHubActionsSsh = 'github-actions-ssh',
|
||||
GitHubActionsK8s = 'github-actions-k8s',
|
||||
}
|
||||
|
||||
export type EditBotProps = {
|
||||
fetchRoles: (input: string) => Promise<string[]>;
|
||||
attempt: Attempt;
|
||||
name: string;
|
||||
onClose: () => void;
|
||||
onEdit: () => void;
|
||||
selectedRoles: string[];
|
||||
setSelectedRoles: Dispatch<SetStateAction<string[]>>;
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ export function ManageCluster() {
|
||||
);
|
||||
}
|
||||
|
||||
export function ManageClusterHeader({ clusterId }: { clusterId: string }) {
|
||||
function ManageClusterHeader({ clusterId }: { clusterId: string }) {
|
||||
return (
|
||||
<FeatureHeader>
|
||||
<Flex alignItems="center">
|
||||
@@ -111,7 +111,7 @@ type ClusterInformationProps = {
|
||||
attempt: Attempt<ClusterInfo>;
|
||||
} & BoxProps;
|
||||
|
||||
export function ClusterInformation({
|
||||
function ClusterInformation({
|
||||
cluster,
|
||||
style,
|
||||
attempt,
|
||||
@@ -170,7 +170,7 @@ export function ClusterInformation({
|
||||
);
|
||||
}
|
||||
|
||||
export const IconBox = styled(Box)`
|
||||
const IconBox = styled(Box)`
|
||||
line-height: 0;
|
||||
padding: ${props => props.theme.space[2]}px;
|
||||
border-radius: ${props => props.theme.radii[3]}px;
|
||||
@@ -178,7 +178,7 @@ export const IconBox = styled(Box)`
|
||||
background: ${props => props.theme.colors.interactive.tonal.neutral[0]};
|
||||
`;
|
||||
|
||||
export const DataItem = ({ title = '', data = null, isLoading = false }) => (
|
||||
const DataItem = ({ title = '', data = null, isLoading = false }) => (
|
||||
<DataItemFlex>
|
||||
<Text typography="body2" bold style={{ width: '136px' }}>
|
||||
{title}:
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2024 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
export { ManageCluster } from './ManageCluster';
|
||||
@@ -103,7 +103,7 @@ export default function useNodes({ clusterId, id }: stores.DocumentNodes) {
|
||||
}
|
||||
|
||||
// sort logins by making 'root' as the first in the list
|
||||
export const sortLogins = (logins: string[]) => {
|
||||
const sortLogins = (logins: string[]) => {
|
||||
const noRoot = logins.filter(l => l !== 'root');
|
||||
noRoot.sort();
|
||||
if (noRoot.length === logins.length) {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
/**
|
||||
* Teleport
|
||||
* Copyright (C) 2023 Gravitational, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import ShareSession from './ShareSession';
|
||||
|
||||
export default ShareSession;
|
||||
@@ -36,7 +36,7 @@ export default function TabsContainer(props: Props) {
|
||||
return <Tabs {...props} parties={ctx.storeParties.state} />;
|
||||
}
|
||||
|
||||
export function Tabs(props: Props & { parties: stores.Parties }) {
|
||||
function Tabs(props: Props & { parties: stores.Parties }) {
|
||||
const {
|
||||
items,
|
||||
parties,
|
||||
|
||||
@@ -22,7 +22,7 @@ import { useStore } from 'shared/libs/stores';
|
||||
|
||||
import ConsoleContext from './consoleContext';
|
||||
|
||||
export const ConsoleReactContext = React.createContext<ConsoleContext>(null);
|
||||
const ConsoleReactContext = React.createContext<ConsoleContext>(null);
|
||||
|
||||
const ConsoleContextProvider: React.FC<
|
||||
PropsWithChildren<{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user